mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
mod: 侧边栏、动态重构,排行改为首页分区,平板、折叠屏、竖屏视频新适配,播放页可隐藏黑边、截图、点踩,弹幕粗细调整,默认关闭后台播放,弹窗接受返回
This commit is contained in:
@@ -8,9 +8,6 @@ class VideoCardHSkeleton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Skeleton(
|
return Skeleton(
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(
|
|
||||||
StyleString.safeSpace, 7, StyleString.safeSpace, 7),
|
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double width =
|
double width =
|
||||||
@@ -27,8 +24,7 @@ class VideoCardHSkeleton extends StatelessWidget {
|
|||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(StyleString.imgRadius.x),
|
BorderRadius.circular(StyleString.imgRadius.x),
|
||||||
),
|
),
|
||||||
@@ -64,17 +60,15 @@ class VideoCardHSkeleton extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context)
|
color:
|
||||||
.colorScheme
|
Theme.of(context).colorScheme.onInverseSurface,
|
||||||
.onInverseSurface,
|
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 13,
|
height: 13,
|
||||||
margin: const EdgeInsets.only(right: 8),
|
margin: const EdgeInsets.only(right: 8),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context)
|
color:
|
||||||
.colorScheme
|
Theme.of(context).colorScheme.onInverseSurface,
|
||||||
.onInverseSurface,
|
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 13,
|
height: 13,
|
||||||
),
|
),
|
||||||
@@ -88,7 +82,6 @@ class VideoCardHSkeleton extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import '../../utils/download.dart';
|
import '../../utils/download.dart';
|
||||||
import '../constants.dart';
|
import '../constants.dart';
|
||||||
import 'network_img_layer.dart';
|
import 'network_img_layer.dart';
|
||||||
@@ -11,9 +14,10 @@ class OverlayPop extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final double imgWidth = MediaQuery.sizeOf(context).width - 8 * 2;
|
final double imgWidth = min(Get.height,Get.width) - 8 * 2;
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
width: imgWidth,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
@@ -70,6 +74,7 @@ class OverlayPop extends StatelessWidget {
|
|||||||
tooltip: '保存封面图',
|
tooltip: '保存封面图',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await DownloadUtils.downloadImg(
|
await DownloadUtils.downloadImg(
|
||||||
|
context,
|
||||||
videoItem.pic != null
|
videoItem.pic != null
|
||||||
? videoItem.pic as String
|
? videoItem.pic as String
|
||||||
: videoItem.cover as String,
|
: videoItem.cover as String,
|
||||||
|
|||||||
@@ -72,21 +72,9 @@ class VideoCardH extends StatelessWidget {
|
|||||||
SmartDialog.showToast(err.toString());
|
SmartDialog.showToast(err.toString());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(
|
|
||||||
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder:
|
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
||||||
(BuildContext context, BoxConstraints boxConstraints) {
|
return Row(
|
||||||
final double width = (boxConstraints.maxWidth -
|
|
||||||
StyleString.cardSpace *
|
|
||||||
6 /
|
|
||||||
MediaQuery.textScalerOf(context).scale(1.0)) /
|
|
||||||
2;
|
|
||||||
return Container(
|
|
||||||
constraints: const BoxConstraints(minHeight: 88),
|
|
||||||
height: width / StyleString.aspectRatio,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -96,8 +84,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
builder: (BuildContext context,
|
builder: (BuildContext context,
|
||||||
BoxConstraints boxConstraints) {
|
BoxConstraints boxConstraints) {
|
||||||
final double maxWidth = boxConstraints.maxWidth;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
final double maxHeight =
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
boxConstraints.maxHeight;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
@@ -140,20 +127,18 @@ class VideoCardH extends StatelessWidget {
|
|||||||
showPubdate: showPubdate,
|
showPubdate: showPubdate,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
)),
|
)),
|
||||||
if (source == 'normal')
|
if (source == 'normal')
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 1,
|
bottom: 0,
|
||||||
right: 10,
|
right: 10,
|
||||||
child: VideoPopupMenu(
|
child: VideoPopupMenu(
|
||||||
size: 30,
|
size: 29,
|
||||||
iconSize: 16,
|
iconSize: 17,
|
||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -182,6 +167,10 @@ class VideoContent extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
String pubdate = showPubdate
|
||||||
|
? Utils.dateFormat(videoItem.pubdate!, formatType: 'day')
|
||||||
|
: '';
|
||||||
|
if (pubdate != '') pubdate += ' ';
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||||
@@ -247,7 +236,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 0,
|
flex: 0,
|
||||||
child: Text(
|
child: Text(
|
||||||
"${showPubdate ? Utils.dateFormat(videoItem.pubdate!, formatType: 'day') + ' ' : ''} ${showOwner ? videoItem.owner.name : ''}",
|
"${pubdate}${showOwner ? videoItem.owner.name : ''}",
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
@@ -276,7 +265,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
if (source == 'normal') const SizedBox(width: 24),
|
if (source == 'normal') const SizedBox(width: 24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 5),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -191,8 +191,8 @@ class VideoCardV extends StatelessWidget {
|
|||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: VideoPopupMenu(
|
child: VideoPopupMenu(
|
||||||
size: 30,
|
size: 29,
|
||||||
iconSize: 16,
|
iconSize: 17,
|
||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
)),
|
)),
|
||||||
]);
|
]);
|
||||||
@@ -224,7 +224,8 @@ class VideoContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const Spacer(),
|
||||||
|
// const SizedBox(height: 2),
|
||||||
VideoStat(
|
VideoStat(
|
||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../http/user.dart';
|
import '../../http/user.dart';
|
||||||
import '../../http/video.dart';
|
import '../../http/video.dart';
|
||||||
@@ -11,7 +12,7 @@ class VideoPopupMenu extends StatelessWidget {
|
|||||||
final double? size;
|
final double? size;
|
||||||
final double? iconSize;
|
final double? iconSize;
|
||||||
final dynamic videoItem;
|
final dynamic videoItem;
|
||||||
final double menuItemHeight = 50;
|
final double menuItemHeight = 45;
|
||||||
|
|
||||||
const VideoPopupMenu({
|
const VideoPopupMenu({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -53,10 +54,44 @@ class VideoPopupMenu extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
SmartDialog.show(
|
Get.toNamed('/member?mid=${videoItem.owner.mid}', arguments: {
|
||||||
useSystem: true,
|
'face': videoItem.owner.face,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
'heroTag': '${videoItem.owner.mid}',
|
||||||
builder: (BuildContext context) {
|
});
|
||||||
|
},
|
||||||
|
value: 'visit',
|
||||||
|
height: menuItemHeight,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(CupertinoIcons.person, size: 16),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text('访问:${videoItem.owner.name}',
|
||||||
|
style: const TextStyle(fontSize: 13))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 不感兴趣
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
onTap: () async {
|
||||||
|
// var res = await VideoHttp.dislike(bvid: videoItem.bvid as String);
|
||||||
|
// SmartDialog.showToast(res['msg']);
|
||||||
|
SmartDialog.showToast("暂未实现");
|
||||||
|
},
|
||||||
|
value: 'dislike',
|
||||||
|
height: menuItemHeight,
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(CupertinoIcons.hand_thumbsdown, size: 16),
|
||||||
|
SizedBox(width: 6),
|
||||||
|
Text('不感兴趣', style: TextStyle(fontSize: 13))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
onTap: () async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(
|
content: Text(
|
||||||
@@ -64,7 +99,7 @@ class VideoPopupMenu extends StatelessWidget {
|
|||||||
'\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'),
|
'\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'点错了',
|
'点错了',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -86,7 +121,7 @@ class VideoPopupMenu extends StatelessWidget {
|
|||||||
blackMidsList.insert(0, videoItem.owner.mid);
|
blackMidsList.insert(0, videoItem.owner.mid);
|
||||||
GStrorage.setting
|
GStrorage.setting
|
||||||
.put(SettingBoxKey.blackMidsList, blackMidsList);
|
.put(SettingBoxKey.blackMidsList, blackMidsList);
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
SmartDialog.showToast(res['msg'] ?? '成功');
|
SmartDialog.showToast(res['msg'] ?? '成功');
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ class Api {
|
|||||||
|
|
||||||
// 视频点踩 web端不支持
|
// 视频点踩 web端不支持
|
||||||
|
|
||||||
|
// 点踩 Post(app端)
|
||||||
|
/// access_key str APP登录Token 必要
|
||||||
|
/// aid num 稿件avid 必要
|
||||||
|
///
|
||||||
|
static const String dislikeVideo = '${HttpString.appBaseUrl}/x/v2/view/dislike';
|
||||||
|
|
||||||
// 投币视频(web端)POST
|
// 投币视频(web端)POST
|
||||||
/// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
/// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
||||||
/// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
/// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
||||||
@@ -338,6 +344,22 @@ class Api {
|
|||||||
//https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/action.md
|
//https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/action.md
|
||||||
static const String shootDanmaku = '/x/v2/dm/post';
|
static const String shootDanmaku = '/x/v2/dm/post';
|
||||||
|
|
||||||
|
// 弹幕屏蔽查询(Get)
|
||||||
|
static const String danmakuFilter = '/x/dm/filter/user';
|
||||||
|
|
||||||
|
// 弹幕屏蔽词添加(Post)
|
||||||
|
// 表单内容:
|
||||||
|
// type: 0(关键词)1(正则)2(用户)
|
||||||
|
// filter: 屏蔽内容
|
||||||
|
// csrf
|
||||||
|
static const String danmakuFilterAdd = '/x/dm/filter/user/add';
|
||||||
|
|
||||||
|
// 弹幕屏蔽词删除(Post)
|
||||||
|
// 表单内容:
|
||||||
|
// ids: 被删除条目编号
|
||||||
|
// csrf
|
||||||
|
static const String danmakuFilterDel = '/x/dm/filter/user/del';
|
||||||
|
|
||||||
// up主分组
|
// up主分组
|
||||||
static const String followUpTag = '/x/relation/tags';
|
static const String followUpTag = '/x/relation/tags';
|
||||||
|
|
||||||
|
|||||||
63
lib/http/danmaku_block.dart
Normal file
63
lib/http/danmaku_block.dart
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import '../models/user/danmaku_block.dart';
|
||||||
|
import 'index.dart';
|
||||||
|
|
||||||
|
class DanmakuFilterHttp {
|
||||||
|
static Future danmakuFilter() async {
|
||||||
|
var res = await Request().get(Api.danmakuFilter);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': DanmakuBlockDataModel.fromJson(res.data['data'])
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future danmakuFilterDel({required int ids}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.danmakuFilterDel,
|
||||||
|
queryParameters: {
|
||||||
|
'ids': ids,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'msg': '操作成功',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future danmakuFilterAdd({required String filter, required int type}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.danmakuFilterAdd,
|
||||||
|
queryParameters: {
|
||||||
|
'type': type,
|
||||||
|
'filter': filter,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': Rule.fromJson(res.data['data']),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,13 @@ import 'index.dart';
|
|||||||
class DynamicsHttp {
|
class DynamicsHttp {
|
||||||
static Future followDynamic({
|
static Future followDynamic({
|
||||||
String? type,
|
String? type,
|
||||||
int? page,
|
|
||||||
String? offset,
|
String? offset,
|
||||||
int? mid,
|
int? mid,
|
||||||
}) async {
|
}) async {
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
'type': type ?? 'all',
|
'type': type ?? 'all',
|
||||||
'page': page ?? 1,
|
|
||||||
'timezone_offset': '-480',
|
'timezone_offset': '-480',
|
||||||
'offset': page == 1 ? '' : offset,
|
'offset': offset,
|
||||||
'features': 'itemOpusStyle'
|
'features': 'itemOpusStyle'
|
||||||
};
|
};
|
||||||
if (mid != -1) {
|
if (mid != -1) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import '../models/user/fav_folder.dart';
|
|||||||
import '../models/video/ai.dart';
|
import '../models/video/ai.dart';
|
||||||
import '../models/video/play/url.dart';
|
import '../models/video/play/url.dart';
|
||||||
import '../models/video_detail_res.dart';
|
import '../models/video_detail_res.dart';
|
||||||
|
import '../utils/id_utils.dart';
|
||||||
import '../utils/recommend_filter.dart';
|
import '../utils/recommend_filter.dart';
|
||||||
import '../utils/storage.dart';
|
import '../utils/storage.dart';
|
||||||
import '../utils/wbi_sign.dart';
|
import '../utils/wbi_sign.dart';
|
||||||
@@ -319,6 +320,29 @@ class VideoHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (取消)点踩
|
||||||
|
static Future dislikeVideo({required String bvid, required bool type}) async {
|
||||||
|
String? accessKey = GStrorage.localCache
|
||||||
|
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||||
|
if (accessKey == null || accessKey == "") {
|
||||||
|
return {'status': false, 'data': [], 'msg': "本操作使用app端接口,请前往【隐私设置】刷新access_key"};
|
||||||
|
}
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.dislikeVideo,
|
||||||
|
queryParameters: {
|
||||||
|
'aid': IdUtils.bv2av(bvid),
|
||||||
|
'dislike': type ? 0 : 1,
|
||||||
|
'access_key': accessKey,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
print(res);
|
||||||
|
if (res.data is! String && res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// (取消)收藏
|
// (取消)收藏
|
||||||
static Future favVideo(
|
static Future favVideo(
|
||||||
{required int aid, String? addIds, String? delIds}) async {
|
{required int aid, String? addIds, String? delIds}) async {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
final List<Map<String, dynamic>> colorThemeTypes = [
|
final List<Map<String, dynamic>> colorThemeTypes = [
|
||||||
{'color': const Color.fromARGB(255, 92, 182, 123), 'label': '默认绿'},
|
{'color': const Color.fromARGB(255, 92, 182, 123), 'label': '默认绿'},
|
||||||
{'color': Colors.pink, 'label': '粉红色'},
|
{'color': const Color.fromARGB(255, 251, 114, 153), 'label': '粉红色'},
|
||||||
{'color': Colors.red, 'label': '红色'},
|
{'color': Colors.red, 'label': '红色'},
|
||||||
{'color': Colors.orange, 'label': '橙色'},
|
{'color': Colors.orange, 'label': '橙色'},
|
||||||
{'color': Colors.amber, 'label': '琥珀色'},
|
{'color': Colors.amber, 'label': '琥珀色'},
|
||||||
|
|||||||
@@ -1,11 +1,56 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../../pages/dynamics/tab/controller.dart';
|
||||||
|
import '../../pages/dynamics/tab/view.dart';
|
||||||
|
|
||||||
enum DynamicsType {
|
enum DynamicsType {
|
||||||
all,
|
all,
|
||||||
video,
|
video,
|
||||||
pgc,
|
pgc,
|
||||||
article,
|
article,
|
||||||
|
up,
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BusinessTypeExtension on DynamicsType {
|
extension BusinessTypeExtension on DynamicsType {
|
||||||
String get values => ['all', 'video', 'pgc', 'article'][index];
|
String get values => ['all', 'video', 'pgc', 'article', 'up'][index];
|
||||||
String get labels => ['全部', '投稿', '番剧', '专栏'][index];
|
String get labels => ['全部', '投稿', '番剧', '专栏', 'Up'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List tabsConfig = [
|
||||||
|
{
|
||||||
|
'value': DynamicsType.all,
|
||||||
|
'label': '全部',
|
||||||
|
'enabled': true,
|
||||||
|
'ctr': Get.put<DynamicsTabController>(DynamicsTabController(), tag: 'all'),
|
||||||
|
'page': const DynamicsTabPage(dynamicsType: 'all'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'value': DynamicsType.video,
|
||||||
|
'label': '投稿',
|
||||||
|
'enabled': true,
|
||||||
|
'ctr':
|
||||||
|
Get.put<DynamicsTabController>(DynamicsTabController(), tag: 'video'),
|
||||||
|
'page': const DynamicsTabPage(dynamicsType: 'video'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'value': DynamicsType.pgc,
|
||||||
|
'label': '番剧',
|
||||||
|
'enabled': true,
|
||||||
|
'ctr': Get.put<DynamicsTabController>(DynamicsTabController(), tag: 'pgc'),
|
||||||
|
'page': const DynamicsTabPage(dynamicsType: 'pgc'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'value': DynamicsType.article,
|
||||||
|
'label': '专栏',
|
||||||
|
'enabled': true,
|
||||||
|
'ctr':
|
||||||
|
Get.put<DynamicsTabController>(DynamicsTabController(), tag: 'article'),
|
||||||
|
'page': const DynamicsTabPage(dynamicsType: 'article'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'value': DynamicsType.up,
|
||||||
|
'label': 'Up',
|
||||||
|
'enabled': true,
|
||||||
|
'ctr': Get.put<DynamicsTabController>(DynamicsTabController(), tag: 'up'),
|
||||||
|
'page': const DynamicsTabPage(dynamicsType: 'up'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@@ -5,30 +5,17 @@ List defaultNavigationBars = [
|
|||||||
'id': 0,
|
'id': 0,
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.home_outlined,
|
Icons.home_outlined,
|
||||||
size: 21,
|
size: 23,
|
||||||
),
|
),
|
||||||
'selectIcon': const Icon(
|
'selectIcon': const Icon(
|
||||||
Icons.home,
|
Icons.home,
|
||||||
size: 21,
|
size: 23,
|
||||||
),
|
),
|
||||||
'label': "首页",
|
'label': "首页",
|
||||||
'count': 0,
|
'count': 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'icon': const Icon(
|
|
||||||
Icons.leaderboard_outlined,
|
|
||||||
size: 21,
|
|
||||||
),
|
|
||||||
'selectIcon': const Icon(
|
|
||||||
Icons.leaderboard,
|
|
||||||
size: 21,
|
|
||||||
),
|
|
||||||
'label': "排行榜",
|
|
||||||
'count': 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': 2,
|
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.motion_photos_on_outlined,
|
Icons.motion_photos_on_outlined,
|
||||||
size: 21,
|
size: 21,
|
||||||
@@ -41,7 +28,7 @@ List defaultNavigationBars = [
|
|||||||
'count': 0,
|
'count': 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 3,
|
'id': 2,
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.video_collection_outlined,
|
Icons.video_collection_outlined,
|
||||||
size: 20,
|
size: 20,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '全站',
|
'label': '全站',
|
||||||
'type': RandType.all,
|
'type': RandType.all,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '0'),
|
||||||
'page': const ZonePage(rid: 0),
|
'page': const ZonePage(rid: 0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84,7 +84,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '国创',
|
'label': '国创',
|
||||||
'type': RandType.creation,
|
'type': RandType.creation,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '168'),
|
||||||
'page': const ZonePage(rid: 168),
|
'page': const ZonePage(rid: 168),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -94,7 +94,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '动画',
|
'label': '动画',
|
||||||
'type': RandType.animation,
|
'type': RandType.animation,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '1'),
|
||||||
'page': const ZonePage(rid: 1),
|
'page': const ZonePage(rid: 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -104,7 +104,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '音乐',
|
'label': '音乐',
|
||||||
'type': RandType.music,
|
'type': RandType.music,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '3'),
|
||||||
'page': const ZonePage(rid: 3),
|
'page': const ZonePage(rid: 3),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -114,7 +114,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '舞蹈',
|
'label': '舞蹈',
|
||||||
'type': RandType.dance,
|
'type': RandType.dance,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '129'),
|
||||||
'page': const ZonePage(rid: 129),
|
'page': const ZonePage(rid: 129),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -124,7 +124,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '游戏',
|
'label': '游戏',
|
||||||
'type': RandType.game,
|
'type': RandType.game,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '4'),
|
||||||
'page': const ZonePage(rid: 4),
|
'page': const ZonePage(rid: 4),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,7 +134,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '知识',
|
'label': '知识',
|
||||||
'type': RandType.knowledge,
|
'type': RandType.knowledge,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '36'),
|
||||||
'page': const ZonePage(rid: 36),
|
'page': const ZonePage(rid: 36),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -144,7 +144,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '科技',
|
'label': '科技',
|
||||||
'type': RandType.technology,
|
'type': RandType.technology,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '188'),
|
||||||
'page': const ZonePage(rid: 188),
|
'page': const ZonePage(rid: 188),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -154,7 +154,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '运动',
|
'label': '运动',
|
||||||
'type': RandType.sport,
|
'type': RandType.sport,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '234'),
|
||||||
'page': const ZonePage(rid: 234),
|
'page': const ZonePage(rid: 234),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -164,7 +164,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '汽车',
|
'label': '汽车',
|
||||||
'type': RandType.car,
|
'type': RandType.car,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '223'),
|
||||||
'page': const ZonePage(rid: 223),
|
'page': const ZonePage(rid: 223),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -174,7 +174,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '生活',
|
'label': '生活',
|
||||||
'type': RandType.life,
|
'type': RandType.life,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '160'),
|
||||||
'page': const ZonePage(rid: 160),
|
'page': const ZonePage(rid: 160),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -184,7 +184,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '美食',
|
'label': '美食',
|
||||||
'type': RandType.food,
|
'type': RandType.food,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '211'),
|
||||||
'page': const ZonePage(rid: 211),
|
'page': const ZonePage(rid: 211),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -194,7 +194,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '动物',
|
'label': '动物',
|
||||||
'type': RandType.animal,
|
'type': RandType.animal,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '217'),
|
||||||
'page': const ZonePage(rid: 217),
|
'page': const ZonePage(rid: 217),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -204,7 +204,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '鬼畜',
|
'label': '鬼畜',
|
||||||
'type': RandType.madness,
|
'type': RandType.madness,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '119'),
|
||||||
'page': const ZonePage(rid: 119),
|
'page': const ZonePage(rid: 119),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -214,7 +214,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '时尚',
|
'label': '时尚',
|
||||||
'type': RandType.fashion,
|
'type': RandType.fashion,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '155'),
|
||||||
'page': const ZonePage(rid: 155),
|
'page': const ZonePage(rid: 155),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -224,7 +224,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '娱乐',
|
'label': '娱乐',
|
||||||
'type': RandType.entertainment,
|
'type': RandType.entertainment,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '5'),
|
||||||
'page': const ZonePage(rid: 5),
|
'page': const ZonePage(rid: 5),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -234,7 +234,7 @@ List tabsConfig = [
|
|||||||
),
|
),
|
||||||
'label': '影视',
|
'label': '影视',
|
||||||
'type': RandType.film,
|
'type': RandType.film,
|
||||||
'ctr': Get.put<ZoneController>,
|
'ctr': Get.put<ZoneController>(ZoneController(), tag: '181'),
|
||||||
'page': const ZonePage(rid: 181),
|
'page': const ZonePage(rid: 181),
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPalaX/pages/rank/index.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPalaX/pages/bangumi/index.dart';
|
import 'package:PiliPalaX/pages/bangumi/index.dart';
|
||||||
@@ -5,11 +6,11 @@ import 'package:PiliPalaX/pages/hot/index.dart';
|
|||||||
import 'package:PiliPalaX/pages/live/index.dart';
|
import 'package:PiliPalaX/pages/live/index.dart';
|
||||||
import 'package:PiliPalaX/pages/rcmd/index.dart';
|
import 'package:PiliPalaX/pages/rcmd/index.dart';
|
||||||
|
|
||||||
enum TabType { live, rcmd, hot, bangumi }
|
enum TabType { live, rcmd, hot, rank, bangumi }
|
||||||
|
|
||||||
extension TabTypeDesc on TabType {
|
extension TabTypeDesc on TabType {
|
||||||
String get description => ['直播', '推荐', '热门', '番剧'][index];
|
String get description => ['直播', '推荐', '热门', '分区', '番剧'][index];
|
||||||
String get id => ['live', 'rcmd', 'hot', 'bangumi'][index];
|
String get id => ['live', 'rcmd', 'hot', 'rank', 'bangumi'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
List tabsConfig = [
|
List tabsConfig = [
|
||||||
@@ -43,6 +44,16 @@ List tabsConfig = [
|
|||||||
'ctr': Get.find<HotController>,
|
'ctr': Get.find<HotController>,
|
||||||
'page': const HotPage(),
|
'page': const HotPage(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(
|
||||||
|
Icons.category_outlined,
|
||||||
|
size: 15,
|
||||||
|
),
|
||||||
|
'label': '分区',
|
||||||
|
'type': TabType.rank,
|
||||||
|
'ctr': Get.find<RankController>,
|
||||||
|
'page': const RankPage(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.play_circle_outlined,
|
Icons.play_circle_outlined,
|
||||||
|
|||||||
15
lib/models/common/up_panel_position.dart
Normal file
15
lib/models/common/up_panel_position.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
enum UpPanelPosition {
|
||||||
|
leftFixed,
|
||||||
|
rightFixed,
|
||||||
|
leftDrawer,
|
||||||
|
rightDrawer,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UpPanelPositionDesc on UpPanelPosition {
|
||||||
|
String get values => ['left_fixed', 'right_fixed', 'left_drawer', 'right_drawer'][index];
|
||||||
|
String get labels => ['左侧常驻','右侧常驻','左侧抽屉','右侧抽屉'][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UpPanelPositionCode on UpPanelPosition {
|
||||||
|
int get code => [0, 1, 2, 3][index];
|
||||||
|
}
|
||||||
94
lib/models/user/danmaku_block.dart
Normal file
94
lib/models/user/danmaku_block.dart
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
class DanmakuBlockDataModel {
|
||||||
|
List<Rule>? rule;
|
||||||
|
String? toast;
|
||||||
|
int? valid;
|
||||||
|
int? ver;
|
||||||
|
|
||||||
|
DanmakuBlockDataModel({this.rule, this.toast, this.valid, this.ver});
|
||||||
|
|
||||||
|
DanmakuBlockDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['rule'] != null) {
|
||||||
|
rule = <Rule>[];
|
||||||
|
json['rule'].forEach((v) {
|
||||||
|
rule!.add(Rule.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
toast = json['toast'];
|
||||||
|
valid = json['valid'];
|
||||||
|
ver = json['ver'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
if (rule != null) {
|
||||||
|
data['rule'] = rule!.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
data['toast'] = toast;
|
||||||
|
data['valid'] = valid;
|
||||||
|
data['ver'] = ver;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rule {
|
||||||
|
int? id;
|
||||||
|
int? mid;
|
||||||
|
int? type;
|
||||||
|
String? filter;
|
||||||
|
String? comment;
|
||||||
|
int? ctime;
|
||||||
|
int? mtime;
|
||||||
|
|
||||||
|
Rule(
|
||||||
|
{this.id,
|
||||||
|
this.mid,
|
||||||
|
this.type,
|
||||||
|
this.filter,
|
||||||
|
this.comment,
|
||||||
|
this.ctime,
|
||||||
|
this.mtime});
|
||||||
|
|
||||||
|
Rule.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['id'];
|
||||||
|
mid = json['mid'];
|
||||||
|
type = json['type'];
|
||||||
|
filter = json['filter'];
|
||||||
|
comment = json['comment'];
|
||||||
|
ctime = json['ctime'];
|
||||||
|
mtime = json['mtime'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['id'] = id;
|
||||||
|
data['mid'] = mid;
|
||||||
|
data['type'] = type;
|
||||||
|
data['filter'] = filter;
|
||||||
|
data['comment'] = comment;
|
||||||
|
data['ctime'] = ctime;
|
||||||
|
data['mtime'] = mtime;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleRule {
|
||||||
|
final int id;
|
||||||
|
final int type;
|
||||||
|
final String filter;
|
||||||
|
SimpleRule(this.id, this.type, this.filter);
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'type': type,
|
||||||
|
'filter': filter,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory SimpleRule.fromMap(Map<String, dynamic> map) {
|
||||||
|
return SimpleRule(
|
||||||
|
map['id'],
|
||||||
|
map['type'],
|
||||||
|
map['filter'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -127,7 +127,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.feedback(),
|
onTap: () => _aboutController.feedback(context),
|
||||||
leading: const Icon(Icons.feedback_outlined),
|
leading: const Icon(Icons.feedback_outlined),
|
||||||
title: const Text('问题反馈'),
|
title: const Text('问题反馈'),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
@@ -174,7 +174,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await CacheManage().clearCacheAll();
|
await CacheManage().clearCacheAll(context);
|
||||||
getCacheSize();
|
getCacheSize();
|
||||||
},
|
},
|
||||||
leading: const Icon(Icons.delete_outline),
|
leading: const Icon(Icons.delete_outline),
|
||||||
@@ -185,17 +185,17 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
title: const Text('导入/导出设置'),
|
title: const Text('导入/导出设置'),
|
||||||
dense: false,
|
dense: false,
|
||||||
leading: const Icon(Icons.import_export_outlined),
|
leading: const Icon(Icons.import_export_outlined),
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return SimpleDialog(
|
return SimpleDialog(
|
||||||
title: const Text('导入/导出设置'),
|
title: const Text('导入/导出设置'),
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('导出设置至剪贴板'),
|
title: const Text('导出设置至剪贴板'),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
String data = await GStrorage.exportAllSettings();
|
String data = await GStrorage.exportAllSettings();
|
||||||
Clipboard.setData(ClipboardData(text: data));
|
Clipboard.setData(ClipboardData(text: data));
|
||||||
SmartDialog.showToast('已复制到剪贴板');
|
SmartDialog.showToast('已复制到剪贴板');
|
||||||
@@ -204,30 +204,35 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('从剪贴板导入设置'),
|
title: const Text('从剪贴板导入设置'),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
ClipboardData? data = await Clipboard.getData('text/plain');
|
ClipboardData? data =
|
||||||
if (data == null || data.text == null || data.text!.isEmpty) {
|
await Clipboard.getData('text/plain');
|
||||||
|
if (data == null ||
|
||||||
|
data.text == null ||
|
||||||
|
data.text!.isEmpty) {
|
||||||
SmartDialog.showToast('剪贴板无数据');
|
SmartDialog.showToast('剪贴板无数据');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SmartDialog.show(
|
if (!context.mounted) return;
|
||||||
useSystem: true,
|
await showDialog(
|
||||||
builder: (BuildContext context) {
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('是否导入如下设置?'),
|
title: const Text('是否导入如下设置?'),
|
||||||
content: Text(data.text!),
|
content: Text(data.text!),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('取消'),
|
child: const Text('取消'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
try {
|
try {
|
||||||
await GStrorage.importAllSettings(data.text!);
|
await GStrorage.importAllSettings(
|
||||||
|
data.text!);
|
||||||
SmartDialog.showToast('导入成功');
|
SmartDialog.showToast('导入成功');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
SmartDialog.showToast('导入失败:$e');
|
SmartDialog.showToast('导入失败:$e');
|
||||||
@@ -249,27 +254,27 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('重置所有设置'),
|
title: const Text('重置所有设置'),
|
||||||
leading: const Icon(Icons.settings_backup_restore_outlined),
|
leading: const Icon(Icons.settings_backup_restore_outlined),
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('重置所有设置'),
|
title: const Text('重置所有设置'),
|
||||||
content: const Text('是否重置所有设置?'),
|
content: const Text('是否重置所有设置?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('取消'),
|
child: const Text('取消'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
Get.back();
|
||||||
GStrorage.setting.clear();
|
GStrorage.setting.clear();
|
||||||
GStrorage.localCache.clear();
|
GStrorage.localCache.clear();
|
||||||
GStrorage.video.clear();
|
GStrorage.video.clear();
|
||||||
SmartDialog.showToast('重置成功');
|
SmartDialog.showToast('重置成功');
|
||||||
SmartDialog.dismiss();
|
|
||||||
},
|
},
|
||||||
child: const Text('确定'),
|
child: const Text('确定'),
|
||||||
),
|
),
|
||||||
@@ -384,11 +389,10 @@ class AboutController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 问题反馈
|
// 问题反馈
|
||||||
feedback() {
|
feedback(BuildContext context) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return SimpleDialog(
|
return SimpleDialog(
|
||||||
title: const Text('问题反馈'),
|
title: const Text('问题反馈'),
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@@ -231,9 +232,29 @@ class BangumiIntroController extends GetxController {
|
|||||||
|
|
||||||
// 分享视频
|
// 分享视频
|
||||||
Future actionShareVideo() async {
|
Future actionShareVideo() async {
|
||||||
var result = await Share.share('${HttpString.baseUrl}/video/$bvid')
|
showDialog(
|
||||||
.whenComplete(() {});
|
context: Get.context!,
|
||||||
|
builder: (context) {
|
||||||
|
String videoUrl = '${HttpString.baseUrl}/video/$bvid';
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('分享方式'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: videoUrl));
|
||||||
|
SmartDialog.showToast('已复制');
|
||||||
|
},
|
||||||
|
child: const Text('复制链接到剪贴板')),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var result =
|
||||||
|
await Share.share(videoUrl).whenComplete(() {});
|
||||||
return result;
|
return result;
|
||||||
|
},
|
||||||
|
child: const Text('分享视频')),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择文件夹
|
// 选择文件夹
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
@@ -283,56 +284,35 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
: bangumiItem!.stat!['danmakus'],
|
: bangumiItem!.stat!['danmakus'],
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
|
if (isLandscape) ...[
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
AreasAndPubTime(
|
||||||
|
widget: widget,
|
||||||
|
bangumiItem: bangumiItem,
|
||||||
|
t: t),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
NewEpDesc(
|
||||||
|
widget: widget,
|
||||||
|
bangumiItem: bangumiItem,
|
||||||
|
t: t),
|
||||||
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: isLandscape ? 2 : 6),
|
SizedBox(height: isLandscape ? 2 : 6),
|
||||||
Row(
|
if (!isLandscape)
|
||||||
children: [
|
AreasAndPubTime(
|
||||||
Text(
|
widget: widget,
|
||||||
!widget.loadingStatus
|
bangumiItem: bangumiItem,
|
||||||
? (widget.bangumiDetail!.areas!
|
t: t),
|
||||||
.isNotEmpty
|
if (!isLandscape)
|
||||||
? widget.bangumiDetail!.areas!
|
NewEpDesc(
|
||||||
.first['name']
|
widget: widget,
|
||||||
: '')
|
bangumiItem: bangumiItem,
|
||||||
: (bangumiItem!.areas!.isNotEmpty
|
t: t),
|
||||||
? bangumiItem!
|
|
||||||
.areas!.first['name']
|
|
||||||
: ''),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: t.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
!widget.loadingStatus
|
|
||||||
? widget.bangumiDetail!
|
|
||||||
.publish!['pub_time_show']
|
|
||||||
: bangumiItem!
|
|
||||||
.publish!['pub_time_show'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: t.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
!widget.loadingStatus
|
|
||||||
? widget.bangumiDetail!.newEp!['desc']
|
|
||||||
: bangumiItem!.newEp!['desc'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: t.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// const SizedBox(height: 10),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
'简介:${!widget.loadingStatus ? widget.bangumiDetail!.evaluate! : bangumiItem!.evaluate!}',
|
'简介:${!widget.loadingStatus ? widget.bangumiDetail!.evaluate! : bangumiItem!.evaluate!}',
|
||||||
maxLines: isLandscape ? 1 : 3,
|
maxLines: isLandscape ? 2 : 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
@@ -525,3 +505,73 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AreasAndPubTime extends StatelessWidget {
|
||||||
|
const AreasAndPubTime({
|
||||||
|
super.key,
|
||||||
|
required this.widget,
|
||||||
|
required this.bangumiItem,
|
||||||
|
required this.t,
|
||||||
|
});
|
||||||
|
|
||||||
|
final BangumiInfo widget;
|
||||||
|
final BangumiInfoModel? bangumiItem;
|
||||||
|
final ThemeData t;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
!widget.loadingStatus
|
||||||
|
? (widget.bangumiDetail!.areas!.isNotEmpty
|
||||||
|
? widget.bangumiDetail!.areas!.first['name']
|
||||||
|
: '')
|
||||||
|
: (bangumiItem!.areas!.isNotEmpty
|
||||||
|
? bangumiItem!.areas!.first['name']
|
||||||
|
: ''),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: t.colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
!widget.loadingStatus
|
||||||
|
? widget.bangumiDetail!.publish!['pub_time_show']
|
||||||
|
: bangumiItem!.publish!['pub_time_show'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: t.colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewEpDesc extends StatelessWidget {
|
||||||
|
const NewEpDesc({
|
||||||
|
super.key,
|
||||||
|
required this.widget,
|
||||||
|
required this.bangumiItem,
|
||||||
|
required this.t,
|
||||||
|
});
|
||||||
|
|
||||||
|
final BangumiInfo widget;
|
||||||
|
final BangumiInfoModel? bangumiItem;
|
||||||
|
final ThemeData t;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Text(
|
||||||
|
!widget.loadingStatus
|
||||||
|
? widget.bangumiDetail!.newEp!['desc']
|
||||||
|
: bangumiItem!.newEp!['desc'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: t.colorScheme.outline,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 268,
|
height: Grid.maxRowWidth * 1,
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFutureFollow,
|
future: _futureBuilderFutureFollow,
|
||||||
builder:
|
builder:
|
||||||
@@ -135,8 +135,8 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Container(
|
return Container(
|
||||||
width: Get.size.width / 3,
|
width: Grid.maxRowWidth / 2,
|
||||||
height: 254,
|
height: Grid.maxRowWidth * 1,
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
left: StyleString.safeSpace,
|
left: StyleString.safeSpace,
|
||||||
right: index ==
|
right: index ==
|
||||||
@@ -219,17 +219,16 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget contentGrid(ctr, bangumiList) {
|
Widget contentGrid(ctr, bangumiList) {
|
||||||
|
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
// 行间距
|
// 行间距
|
||||||
mainAxisSpacing: StyleString.cardSpace - 2,
|
mainAxisSpacing: StyleString.cardSpace - 2,
|
||||||
// 列间距
|
// 列间距
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
// 最大宽度
|
// 最大宽度
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth / 3 * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth / 3 * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth / 3 * 2, StyleString.safeSpace) / 0.65+
|
childAspectRatio: 0.65,
|
||||||
MediaQuery.textScalerOf(context).scale(60),
|
mainAxisExtent: MediaQuery.textScalerOf(context).scale(60),
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
late double fontSizeVal;
|
late double fontSizeVal;
|
||||||
late double danmakuDurationVal;
|
late double danmakuDurationVal;
|
||||||
late double strokeWidth;
|
late double strokeWidth;
|
||||||
|
late int fontWeight;
|
||||||
int latestAddedPosition = -1;
|
int latestAddedPosition = -1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -68,6 +69,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
opacityVal = playerController.opacityVal;
|
opacityVal = playerController.opacityVal;
|
||||||
fontSizeVal = playerController.fontSizeVal;
|
fontSizeVal = playerController.fontSizeVal;
|
||||||
strokeWidth = playerController.strokeWidth;
|
strokeWidth = playerController.strokeWidth;
|
||||||
|
fontWeight = playerController.fontWeight;
|
||||||
danmakuDurationVal = playerController.danmakuDurationVal;
|
danmakuDurationVal = playerController.danmakuDurationVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +134,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
},
|
},
|
||||||
option: DanmakuOption(
|
option: DanmakuOption(
|
||||||
fontSize: 15 * fontSizeVal,
|
fontSize: 15 * fontSizeVal,
|
||||||
|
fontWeight: fontWeight,
|
||||||
area: showArea,
|
area: showArea,
|
||||||
opacity: opacityVal,
|
opacity: opacityVal,
|
||||||
hideTop: blockTypes.contains(5),
|
hideTop: blockTypes.contains(5),
|
||||||
|
|||||||
182
lib/pages/danmaku_block/index.dart
Normal file
182
lib/pages/danmaku_block/index.dart
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
|
||||||
|
import '../../http/danmaku_block.dart';
|
||||||
|
import '../../models/user/danmaku_block.dart';
|
||||||
|
|
||||||
|
class DanmakuBlockPage extends StatefulWidget {
|
||||||
|
const DanmakuBlockPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DanmakuBlockPage> createState() => _DanmakuBlockPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
|
||||||
|
final DanmakuBlockController _danmakuBlockController =
|
||||||
|
Get.put(DanmakuBlockController());
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_danmakuBlockController.queryDanmakuFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
List<Map<String, dynamic>> simpleRuleList =
|
||||||
|
_danmakuBlockController.danmakuRules.map<Map<String, dynamic>>((e) {
|
||||||
|
return SimpleRule(e.id!, e.type!, e.filter!).toMap();
|
||||||
|
}).toList();
|
||||||
|
setting.put(SettingBoxKey.danmakuFilterRule, simpleRuleList);
|
||||||
|
scrollController.removeListener(() {});
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Obx(
|
||||||
|
() => TabBar(
|
||||||
|
controller: _danmakuBlockController.tabController,
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
tabs: [
|
||||||
|
Tab(text: '文本(${_danmakuBlockController.textRules.length})'),
|
||||||
|
Tab(text: '正则(${_danmakuBlockController.regexRules.length})'),
|
||||||
|
Tab(text: '用户(${_danmakuBlockController.userRules.length})'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: RefreshIndicator(
|
||||||
|
onRefresh: () async =>
|
||||||
|
await _danmakuBlockController.queryDanmakuFilter(),
|
||||||
|
child: TabBarView(
|
||||||
|
controller: _danmakuBlockController.tabController,
|
||||||
|
children: [
|
||||||
|
Obx(() => tabViewBuilder(0, _danmakuBlockController.textRules)),
|
||||||
|
Obx(() => tabViewBuilder(1, _danmakuBlockController.regexRules)),
|
||||||
|
Obx(() => tabViewBuilder(2, _danmakuBlockController.userRules)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Widget tabViewBuilder(int index, List<SimpleRule> list) {
|
||||||
|
return ListView.builder(
|
||||||
|
controller: scrollController,
|
||||||
|
itemCount: list.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(
|
||||||
|
list[index].filter,
|
||||||
|
style: Theme.of(context).textTheme.subtitle1,
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () async {
|
||||||
|
await _danmakuBlockController.danmakuFilterDel(
|
||||||
|
1, list[index].id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DanmakuBlockController extends GetxController
|
||||||
|
with GetTickerProviderStateMixin {
|
||||||
|
RxList<Rule> danmakuRules = <Rule>[].obs;
|
||||||
|
RxList<SimpleRule> textRules = <SimpleRule>[].obs;
|
||||||
|
RxList<SimpleRule> regexRules = <SimpleRule>[].obs;
|
||||||
|
RxList<SimpleRule> userRules = <SimpleRule>[].obs;
|
||||||
|
late TabController tabController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
tabController = TabController(length: 3, vsync: this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
tabController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future queryDanmakuFilter() async {
|
||||||
|
var result = await DanmakuFilterHttp.danmakuFilter();
|
||||||
|
if (result['status']) {
|
||||||
|
danmakuRules.value = result['data'].rule;
|
||||||
|
danmakuRules.map((e) {
|
||||||
|
SimpleRule simpleRule = SimpleRule(e.id!, e.type!, e.filter!);
|
||||||
|
switch (e.type!) {
|
||||||
|
case 0:
|
||||||
|
textRules.add(simpleRule);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
regexRules.add(simpleRule);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
userRules.add(simpleRule);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SmartDialog.showToast('未知的规则类型:${e.type},内容为:${e.filter}');
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
SmartDialog.showToast(result['data'].toast);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future danmakuFilterDel(int type, int id) async {
|
||||||
|
var result = await DanmakuFilterHttp.danmakuFilterDel(ids: id);
|
||||||
|
if (result['status']) {
|
||||||
|
danmakuRules.removeWhere((e) => e.id == id);
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
textRules.removeWhere((e) => e.id == id);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
regexRules.removeWhere((e) => e.id == id);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
userRules.removeWhere((e) => e.id == id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future danmakuFilterAdd({required String filter, required int type}) async {
|
||||||
|
var result =
|
||||||
|
await DanmakuFilterHttp.danmakuFilterAdd(filter: filter, type: type);
|
||||||
|
if (result['status']) {
|
||||||
|
Rule data = result['data'];
|
||||||
|
danmakuRules.add(data);
|
||||||
|
SimpleRule simpleRule = SimpleRule(data.id!, data.type!, data.filter!);
|
||||||
|
switch (data.type!) {
|
||||||
|
case 0:
|
||||||
|
textRules.add(simpleRule);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
regexRules.add(simpleRule);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
userRules.add(simpleRule);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SmartDialog.showToast('添加成功');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
|
import 'package:PiliPalaX/pages/dynamics/tab/index.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -17,39 +18,17 @@ import 'package:PiliPalaX/utils/id_utils.dart';
|
|||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
|
|
||||||
class DynamicsController extends GetxController {
|
class DynamicsController extends GetxController
|
||||||
int page = 1;
|
with GetTickerProviderStateMixin {
|
||||||
String? offset = '';
|
String? offset = '';
|
||||||
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs;
|
|
||||||
Rx<DynamicsType> dynamicsType = DynamicsType.values[0].obs;
|
|
||||||
RxString dynamicsTypeLabel = '全部'.obs;
|
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
Rx<FollowUpModel> upData = FollowUpModel().obs;
|
Rx<FollowUpModel> upData = FollowUpModel().obs;
|
||||||
// 默认获取全部动态
|
// 默认获取全部动态
|
||||||
RxInt mid = (-1).obs;
|
RxInt mid = (-1).obs;
|
||||||
Rx<UpItem> upInfo = UpItem().obs;
|
Rx<UpItem> upInfo = UpItem().obs;
|
||||||
List filterTypeList = [
|
late TabController tabController;
|
||||||
{
|
RxList<int> tempBannedList = <int>[].obs;
|
||||||
'label': DynamicsType.all.labels,
|
late List<Widget> tabsPageList;
|
||||||
'value': DynamicsType.all,
|
|
||||||
'enabled': true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': DynamicsType.video.labels,
|
|
||||||
'value': DynamicsType.video,
|
|
||||||
'enabled': true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': DynamicsType.pgc.labels,
|
|
||||||
'value': DynamicsType.pgc,
|
|
||||||
'enabled': true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': DynamicsType.article.labels,
|
|
||||||
'value': DynamicsType.article,
|
|
||||||
'enabled': true
|
|
||||||
},
|
|
||||||
];
|
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
RxInt initialValue = 0.obs;
|
RxInt initialValue = 0.obs;
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
@@ -63,53 +42,23 @@ class DynamicsController extends GetxController {
|
|||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
userLogin.value = userInfo != null;
|
userLogin.value = userInfo != null;
|
||||||
super.onInit();
|
super.onInit();
|
||||||
initialValue.value =
|
|
||||||
setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0);
|
tabController = TabController(
|
||||||
dynamicsType = DynamicsType.values[initialValue.value].obs;
|
length: tabsConfig.length,
|
||||||
|
vsync: this,
|
||||||
|
initialIndex:
|
||||||
|
setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0));
|
||||||
|
tabsPageList = tabsConfig.map((e) {
|
||||||
|
return e['page'] as Widget;
|
||||||
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future queryFollowDynamic({type = 'init'}) async {
|
void refreshNotifier() {
|
||||||
if (!userLogin.value) {
|
queryFollowUp();
|
||||||
return {'status': false, 'msg': '账号未登录'};
|
|
||||||
}
|
|
||||||
if (type == 'init') {
|
|
||||||
dynamicsList.clear();
|
|
||||||
}
|
|
||||||
// 下拉刷新数据渲染时会触发onLoad
|
|
||||||
if (type == 'onLoad' && page == 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isLoadingDynamic.value = true;
|
|
||||||
var res = await DynamicsHttp.followDynamic(
|
|
||||||
page: type == 'init' ? 1 : page,
|
|
||||||
type: dynamicsType.value.values,
|
|
||||||
offset: offset,
|
|
||||||
mid: mid.value,
|
|
||||||
);
|
|
||||||
isLoadingDynamic.value = false;
|
|
||||||
if (res['status']) {
|
|
||||||
if (type == 'onLoad' && res['data'].items.isEmpty) {
|
|
||||||
SmartDialog.showToast('没有更多了');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (type == 'init') {
|
|
||||||
dynamicsList.value = res['data'].items;
|
|
||||||
} else {
|
|
||||||
dynamicsList.addAll(res['data'].items);
|
|
||||||
}
|
|
||||||
offset = res['data'].offset;
|
|
||||||
page++;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectType(value) async {
|
onSelectType(value) async {
|
||||||
dynamicsType.value = filterTypeList[value]['value'];
|
|
||||||
dynamicsList.value = [DynamicItemModel()];
|
|
||||||
page = 1;
|
|
||||||
initialValue.value = value;
|
initialValue.value = value;
|
||||||
await queryFollowDynamic();
|
|
||||||
scrollController.jumpTo(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pushDetail(item, floor, {action = 'all'}) async {
|
pushDetail(item, floor, {action = 'all'}) async {
|
||||||
@@ -276,21 +225,27 @@ class DynamicsController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSelectUp(mid) async {
|
onSelectUp(mid) async {
|
||||||
dynamicsType.value = DynamicsType.values[0];
|
if (mid == this.mid.value) {
|
||||||
dynamicsList.value = [DynamicItemModel()];
|
this.mid.refresh();
|
||||||
page = 1;
|
return;
|
||||||
queryFollowDynamic();
|
}
|
||||||
|
this.mid.value = mid;
|
||||||
|
tabController.index = (mid == -1 ? 0 : 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRefresh() async {
|
onRefresh() async {
|
||||||
page = 1;
|
|
||||||
print('onRefresh');
|
print('onRefresh');
|
||||||
await queryFollowUp();
|
print(tabsConfig[tabController.index]['ctr']);
|
||||||
await queryFollowDynamic();
|
await Future.wait(<Future>[
|
||||||
|
queryFollowUp(),
|
||||||
|
tabsConfig[tabController.index]['ctr'].onRefresh()
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回顶部并刷新
|
// 返回顶部并刷新
|
||||||
void animateToTop() async {
|
void animateToTop() async {
|
||||||
|
tabsConfig[tabController.index]['ctr'].animateToTop();
|
||||||
|
if (!scrollController.hasClients) return;
|
||||||
if (scrollController.offset >=
|
if (scrollController.offset >=
|
||||||
MediaQuery.of(Get.context!).size.height * 5) {
|
MediaQuery.of(Get.context!).size.height * 5) {
|
||||||
scrollController.jumpTo(0);
|
scrollController.jumpTo(0);
|
||||||
@@ -299,14 +254,4 @@ class DynamicsController extends GetxController {
|
|||||||
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置搜索
|
|
||||||
void resetSearch() {
|
|
||||||
mid.value = -1;
|
|
||||||
dynamicsType.value = DynamicsType.values[0];
|
|
||||||
initialValue.value = 0;
|
|
||||||
SmartDialog.showToast('还原默认加载');
|
|
||||||
dynamicsList.value = [DynamicItemModel()];
|
|
||||||
queryFollowDynamic();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
76
lib/pages/dynamics/tab/controller.dart
Normal file
76
lib/pages/dynamics/tab/controller.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
// import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
import '../../../http/dynamics.dart';
|
||||||
|
import '../../../models/dynamics/result.dart';
|
||||||
|
// import '../../../utils/storage.dart';
|
||||||
|
|
||||||
|
class DynamicsTabController extends GetxController {
|
||||||
|
String offset = '';
|
||||||
|
ScrollController scrollController = ScrollController();
|
||||||
|
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs;
|
||||||
|
RxBool isLoadingMore = false.obs;
|
||||||
|
String dynamicsType = 'all';
|
||||||
|
// Box userInfoCache = GStrorage.userInfo;
|
||||||
|
// bool userLogin = false;
|
||||||
|
int mid = -1;
|
||||||
|
|
||||||
|
Future queryFollowDynamic(String type, String dynamicsType, int? mid) async {
|
||||||
|
this.dynamicsType = dynamicsType;
|
||||||
|
if (mid != null) this.mid = mid;
|
||||||
|
if (type != 'onLoad') {
|
||||||
|
dynamicsList.clear();
|
||||||
|
offset = '';
|
||||||
|
}
|
||||||
|
// // 下拉刷新数据渲染时会触发onLoad
|
||||||
|
// if (type == 'onLoad' && page == 1) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
isLoadingMore.value = true;
|
||||||
|
var res = await DynamicsHttp.followDynamic(
|
||||||
|
type: dynamicsType == "up" ? "all" : dynamicsType,
|
||||||
|
offset: offset,
|
||||||
|
mid: dynamicsType == "up" ? mid : -1,
|
||||||
|
);
|
||||||
|
isLoadingMore.value = false;
|
||||||
|
if (res['status']) {
|
||||||
|
if (type == 'onLoad' && res['data'].items.isEmpty) {
|
||||||
|
SmartDialog.showToast('没有更多了');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type == 'onLoad') {
|
||||||
|
dynamicsList.addAll(res['data'].items);
|
||||||
|
} else {
|
||||||
|
dynamicsList.value = res['data'].items;
|
||||||
|
}
|
||||||
|
// print('dynamicsList: $dynamicsList');
|
||||||
|
dynamicsList.refresh();
|
||||||
|
offset = res['data'].offset;
|
||||||
|
// print("page: $page[dynamicsType]!");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
Future onRefresh() async {
|
||||||
|
await queryFollowDynamic('onRefresh', dynamicsType, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上拉加载
|
||||||
|
Future onLoad() async {
|
||||||
|
await queryFollowDynamic('onLoad', dynamicsType, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回顶部并刷新
|
||||||
|
void animateToTop() async {
|
||||||
|
if (scrollController.offset >=
|
||||||
|
MediaQuery.of(Get.context!).size.height * 5) {
|
||||||
|
scrollController.jumpTo(0);
|
||||||
|
} else {
|
||||||
|
await scrollController.animateTo(0,
|
||||||
|
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/pages/dynamics/tab/index.dart
Normal file
4
lib/pages/dynamics/tab/index.dart
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
library dynamics.tab;
|
||||||
|
|
||||||
|
export './controller.dart';
|
||||||
|
export './view.dart';
|
||||||
234
lib/pages/dynamics/tab/view.dart
Normal file
234
lib/pages/dynamics/tab/view.dart
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:PiliPalaX/common/constants.dart';
|
||||||
|
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||||
|
import 'package:PiliPalaX/pages/home/index.dart';
|
||||||
|
import 'package:PiliPalaX/pages/main/index.dart';
|
||||||
|
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
||||||
|
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||||
|
import 'package:waterfall_flow/waterfall_flow.dart';
|
||||||
|
|
||||||
|
import '../../../common/skeleton/dynamic_card.dart';
|
||||||
|
import '../../../models/dynamics/result.dart';
|
||||||
|
import '../../../utils/grid.dart';
|
||||||
|
|
||||||
|
import '../index.dart';
|
||||||
|
import '../widgets/dynamic_panel.dart';
|
||||||
|
import 'controller.dart';
|
||||||
|
|
||||||
|
class DynamicsTabPage extends StatefulWidget {
|
||||||
|
const DynamicsTabPage({Key? key, required this.dynamicsType})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final String dynamicsType;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DynamicsTabPage> createState() => _DynamicsTabPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DynamicsTabPageState extends State<DynamicsTabPage>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
late DynamicsTabController _dynamicsTabController;
|
||||||
|
late Future _futureBuilderFuture;
|
||||||
|
late ScrollController scrollController;
|
||||||
|
late bool dynamicsWaterfallFlow;
|
||||||
|
late final DynamicsController dynamicsController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_dynamicsTabController =
|
||||||
|
Get.put(DynamicsTabController(), tag: widget.dynamicsType);
|
||||||
|
dynamicsController = Get.find<DynamicsController>();
|
||||||
|
|
||||||
|
_futureBuilderFuture = _dynamicsTabController.queryFollowDynamic(
|
||||||
|
'init', widget.dynamicsType, dynamicsController.mid.value);
|
||||||
|
scrollController = _dynamicsTabController.scrollController
|
||||||
|
..addListener(() {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
if (!_dynamicsTabController.isLoadingMore.value) {
|
||||||
|
EasyThrottle.throttle('_dynamicsTabController_onLoad',
|
||||||
|
const Duration(milliseconds: 500), () {
|
||||||
|
_dynamicsTabController.isLoadingMore.value = true;
|
||||||
|
_dynamicsTabController.onLoad();
|
||||||
|
_dynamicsTabController.isLoadingMore.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dynamicsController.mid.listen((mid) {
|
||||||
|
print('midListen: $mid');
|
||||||
|
scrollController.jumpTo(0);
|
||||||
|
_futureBuilderFuture = _dynamicsTabController.queryFollowDynamic(
|
||||||
|
'init', widget.dynamicsType, mid);
|
||||||
|
});
|
||||||
|
dynamicsWaterfallFlow = GStrorage.setting
|
||||||
|
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
scrollController.removeListener(() {});
|
||||||
|
dynamicsController.mid.close();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
// print(widget.dynamicsType + widget.mid.value.toString());
|
||||||
|
return RefreshIndicator(
|
||||||
|
// key:
|
||||||
|
// ValueKey<String>(widget.dynamicsType + widget.mid.value.toString()),
|
||||||
|
onRefresh: () async {
|
||||||
|
dynamicsWaterfallFlow = GStrorage.setting
|
||||||
|
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||||
|
await Future.wait(<Future>[
|
||||||
|
_dynamicsTabController.onRefresh(),
|
||||||
|
dynamicsController.queryFollowUp()
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
child: CustomScrollView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
controller: _dynamicsTabController.scrollController,
|
||||||
|
slivers: [
|
||||||
|
FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
// print(snapshot);
|
||||||
|
// print(widget.dynamicsType + "${widget.mid?.value}");
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
if (snapshot.data == null) {
|
||||||
|
return const NoData();
|
||||||
|
}
|
||||||
|
Map data = snapshot.data;
|
||||||
|
// print('data: $data');
|
||||||
|
if (data['status']) {
|
||||||
|
List<DynamicItemModel> list =
|
||||||
|
_dynamicsTabController.dynamicsList;
|
||||||
|
// print('list: $list');
|
||||||
|
return Obx(
|
||||||
|
() {
|
||||||
|
if (list.isEmpty) {
|
||||||
|
if (_dynamicsTabController.isLoadingMore.value) {
|
||||||
|
return skeleton();
|
||||||
|
}
|
||||||
|
return const NoData();
|
||||||
|
}
|
||||||
|
if (!dynamicsWaterfallFlow) {
|
||||||
|
return SliverCrossAxisGroup(
|
||||||
|
slivers: [
|
||||||
|
const SliverFillRemaining(),
|
||||||
|
SliverConstrainedCrossAxis(
|
||||||
|
maxExtent: Grid.maxRowWidth * 2,
|
||||||
|
sliver: SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
if ((dynamicsController
|
||||||
|
.tabController.index ==
|
||||||
|
4 &&
|
||||||
|
dynamicsController.mid.value !=
|
||||||
|
-1) ||
|
||||||
|
!dynamicsController.tempBannedList
|
||||||
|
.contains(list[index]
|
||||||
|
.modules
|
||||||
|
?.moduleAuthor
|
||||||
|
?.mid)) {
|
||||||
|
return DynamicPanel(
|
||||||
|
item: list[index]);
|
||||||
|
}
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
childCount: list.length,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SliverFillRemaining(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SliverWaterfallFlow.extent(
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
//cacheExtent: 0.0,
|
||||||
|
crossAxisSpacing: StyleString.cardSpace / 2,
|
||||||
|
mainAxisSpacing: StyleString.cardSpace / 2,
|
||||||
|
|
||||||
|
lastChildLayoutTypeBuilder: (index) =>
|
||||||
|
index == list.length
|
||||||
|
? LastChildLayoutType.foot
|
||||||
|
: LastChildLayoutType.none,
|
||||||
|
children: [
|
||||||
|
if (dynamicsController.tabController.index == 4 &&
|
||||||
|
dynamicsController.mid.value != -1) ...[
|
||||||
|
for (var i in list) DynamicPanel(item: i),
|
||||||
|
] else ...[
|
||||||
|
for (var i in list)
|
||||||
|
if (!dynamicsController.tempBannedList
|
||||||
|
.contains(i.modules?.moduleAuthor?.mid))
|
||||||
|
DynamicPanel(item: i),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () {
|
||||||
|
// setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_dynamicsTabController.queryFollowDynamic(
|
||||||
|
'init',
|
||||||
|
widget.dynamicsType,
|
||||||
|
dynamicsController.mid.value);
|
||||||
|
// });
|
||||||
|
dynamicsController.onRefresh();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return skeleton();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget skeleton() {
|
||||||
|
if (!dynamicsWaterfallFlow) {
|
||||||
|
return SliverCrossAxisGroup(slivers: [
|
||||||
|
const SliverFillRemaining(),
|
||||||
|
SliverConstrainedCrossAxis(
|
||||||
|
maxExtent: Grid.maxRowWidth * 2,
|
||||||
|
sliver: SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const DynamicCardSkeleton();
|
||||||
|
}, childCount: 10)),
|
||||||
|
),
|
||||||
|
const SliverFillRemaining()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
crossAxisSpacing: StyleString.cardSpace / 2,
|
||||||
|
mainAxisSpacing: StyleString.cardSpace / 2,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio,
|
||||||
|
mainAxisExtent: 50),
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const DynamicCardSkeleton();
|
||||||
|
}, childCount: 10),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,15 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
|
import 'package:PiliPalaX/models/common/dynamics_type.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:PiliPalaX/models/common/up_panel_position.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:waterfall_flow/waterfall_flow.dart';
|
|
||||||
import 'package:PiliPalaX/common/skeleton/dynamic_card.dart';
|
|
||||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
|
||||||
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
|
||||||
import 'package:PiliPalaX/models/dynamics/result.dart';
|
|
||||||
import 'package:PiliPalaX/pages/main/index.dart';
|
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
|
||||||
import '../../common/constants.dart';
|
|
||||||
import '../../utils/grid.dart';
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/dynamic_panel.dart';
|
|
||||||
import 'widgets/up_panel.dart';
|
import 'widgets/up_panel.dart';
|
||||||
|
|
||||||
class DynamicsPage extends StatefulWidget {
|
class DynamicsPage extends StatefulWidget {
|
||||||
@@ -29,12 +20,12 @@ class DynamicsPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DynamicsPageState extends State<DynamicsPage>
|
class _DynamicsPageState extends State<DynamicsPage>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin {
|
||||||
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
||||||
late Future _futureBuilderFuture;
|
|
||||||
late Future _futureBuilderFutureUp;
|
late Future _futureBuilderFutureUp;
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
late ScrollController scrollController;
|
late ScrollController scrollController;
|
||||||
|
late UpPanelPosition upPanelPosition;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
@@ -42,269 +33,125 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_futureBuilderFuture = _dynamicsController.queryFollowDynamic();
|
|
||||||
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
|
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
|
||||||
scrollController = _dynamicsController.scrollController;
|
// _dynamicsController.tabController =
|
||||||
StreamController<bool> mainStream =
|
// TabController(vsync: this, length: DynamicsType.values.length);
|
||||||
Get.find<MainController>().bottomBarStream;
|
// ..addListener(() {
|
||||||
scrollController.addListener(
|
// if (!_dynamicsController.tabController.indexIsChanging) {
|
||||||
() async {
|
// // if (!mounted) return;
|
||||||
if (scrollController.position.pixels >=
|
// // print('indexChanging: ${_dynamicsController.tabController.index}');
|
||||||
scrollController.position.maxScrollExtent - 200) {
|
// _dynamicsController
|
||||||
EasyThrottle.throttle(
|
// .onSelectType(_dynamicsController.tabController.index);
|
||||||
'queryFollowDynamic', const Duration(seconds: 1), () {
|
// }
|
||||||
_dynamicsController.queryFollowDynamic(type: 'onLoad');
|
// });
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
final ScrollDirection direction =
|
|
||||||
scrollController.position.userScrollDirection;
|
|
||||||
if (direction == ScrollDirection.forward) {
|
|
||||||
mainStream.add(true);
|
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
|
||||||
mainStream.add(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
_dynamicsController.userLogin.listen((status) {
|
_dynamicsController.userLogin.listen((status) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_futureBuilderFuture = _dynamicsController.queryFollowDynamic();
|
|
||||||
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
|
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
upPanelPosition = UpPanelPosition.values[setting.get(
|
||||||
|
SettingBoxKey.upPanelPosition,
|
||||||
|
defaultValue: UpPanelPosition.leftFixed.code)];
|
||||||
|
print('upPanelPosition: $upPanelPosition');
|
||||||
|
scrollController = _dynamicsController.scrollController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
scrollController.removeListener(() {});
|
_dynamicsController.tabController.removeListener(() {});
|
||||||
|
_dynamicsController.tabController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Widget upPanelPart() {
|
||||||
Widget build(BuildContext context) {
|
return Padding(
|
||||||
super.build(context);
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
return Scaffold(
|
child: Container(
|
||||||
appBar: AppBar(
|
//抽屉模式增加底色
|
||||||
elevation: 0,
|
color: upPanelPosition.code > 1? Theme.of(context).colorScheme.surface: Colors.transparent,
|
||||||
scrolledUnderElevation: 0,
|
width: 56,
|
||||||
titleSpacing: 0,
|
child: FutureBuilder(
|
||||||
title: SizedBox(
|
|
||||||
height: 34,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Obx(() {
|
|
||||||
if (_dynamicsController.mid.value != -1 &&
|
|
||||||
_dynamicsController.upInfo.value.uname != null) {
|
|
||||||
return SizedBox(
|
|
||||||
height: 36,
|
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
transitionBuilder:
|
|
||||||
(Widget child, Animation<double> animation) {
|
|
||||||
return ScaleTransition(
|
|
||||||
scale: animation, child: child);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'${_dynamicsController.upInfo.value.uname!}的动态',
|
|
||||||
key: ValueKey<String>(
|
|
||||||
_dynamicsController.upInfo.value.uname!),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelLarge!
|
|
||||||
.fontSize,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Obx(
|
|
||||||
() => _dynamicsController.userLogin.value
|
|
||||||
? Visibility(
|
|
||||||
visible: _dynamicsController.mid.value == -1,
|
|
||||||
child: Theme(
|
|
||||||
data: ThemeData(
|
|
||||||
splashColor:
|
|
||||||
Colors.transparent, // 点击时的水波纹颜色设置为透明
|
|
||||||
highlightColor:
|
|
||||||
Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
|
||||||
),
|
|
||||||
child: CustomSlidingSegmentedControl<int>(
|
|
||||||
initialValue:
|
|
||||||
_dynamicsController.initialValue.value,
|
|
||||||
children: {
|
|
||||||
0: Text(
|
|
||||||
'全部',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize),
|
|
||||||
),
|
|
||||||
1: Text('投稿',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize)),
|
|
||||||
2: Text('番剧',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize)),
|
|
||||||
3: Text('专栏',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize)),
|
|
||||||
},
|
|
||||||
padding: 13.0,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surfaceVariant
|
|
||||||
.withOpacity(0.7),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
thumbDecoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.background,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
onValueChanged: (v) {
|
|
||||||
feedBack();
|
|
||||||
_dynamicsController.onSelectType(v);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Text('动态',
|
|
||||||
style: Theme.of(context).textTheme.titleMedium),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: RefreshIndicator(
|
|
||||||
onRefresh: () => _dynamicsController.onRefresh(),
|
|
||||||
child: CustomScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
controller: _dynamicsController.scrollController,
|
|
||||||
slivers: [
|
|
||||||
FutureBuilder(
|
|
||||||
future: _futureBuilderFutureUp,
|
future: _futureBuilderFutureUp,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
Map data = snapshot.data;
|
Map data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(() => UpPanel(_dynamicsController.upData.value));
|
return Obx(() => UpPanel(
|
||||||
|
_dynamicsController.upData.value,
|
||||||
|
scrollController));
|
||||||
} else {
|
} else {
|
||||||
return const SliverToBoxAdapter(
|
return const SizedBox();
|
||||||
child: SizedBox(height: 80),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return const SliverToBoxAdapter(
|
return const SizedBox(
|
||||||
child: SizedBox(
|
width: 56,
|
||||||
height: 90,
|
|
||||||
child: UpPanelSkeleton(),
|
child: UpPanelSkeleton(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
print('upPanelPosition1: $upPanelPosition');
|
||||||
|
super.build(context);
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
appBar: AppBar(
|
||||||
|
toolbarHeight: 50,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
title: SizedBox(
|
||||||
|
height: 50,
|
||||||
|
child: TabBar(
|
||||||
|
controller: _dynamicsController.tabController,
|
||||||
|
isScrollable: true,
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
dividerHeight: 0,
|
||||||
|
tabAlignment: TabAlignment.center,
|
||||||
|
indicatorColor: Theme.of(context).colorScheme.primary,
|
||||||
|
labelColor: Theme.of(context).colorScheme.primary,
|
||||||
|
unselectedLabelColor: Theme.of(context).colorScheme.onSurface,
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
tabs: DynamicsType.values
|
||||||
future: _futureBuilderFuture,
|
.map((e) => Tab(text: e.labels))
|
||||||
builder: (context, snapshot) {
|
.toList(),
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
onTap: (index) {
|
||||||
if (snapshot.data == null) {
|
print('index: $index');
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
feedBack();
|
||||||
|
tabsConfig[_dynamicsController.tabController.index]['ctr'].animateToTop();
|
||||||
|
// _dynamicsController.tabController
|
||||||
|
// _dynamicsController.tabController.index = index;
|
||||||
|
// _dynamicsController.onSelectType(index);
|
||||||
|
// _
|
||||||
}
|
}
|
||||||
Map data = snapshot.data;
|
)),
|
||||||
if (data['status']) {
|
|
||||||
List<DynamicItemModel> list =
|
|
||||||
_dynamicsController.dynamicsList;
|
|
||||||
return Obx(
|
|
||||||
() {
|
|
||||||
if (list.isEmpty) {
|
|
||||||
if (_dynamicsController.isLoadingDynamic.value) {
|
|
||||||
return skeleton();
|
|
||||||
} else {
|
|
||||||
return const NoData();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return SliverWaterfallFlow.extent(
|
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
|
||||||
//cacheExtent: 0.0,
|
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
|
||||||
|
|
||||||
/// follow max child trailing layout offset and layout with full cross axis extend
|
|
||||||
/// last child as loadmore item/no more item in [GridView] and [WaterfallFlow]
|
|
||||||
/// with full cross axis extend
|
|
||||||
// LastChildLayoutType.fullCrossAxisExtend,
|
|
||||||
|
|
||||||
/// as foot at trailing and layout with full cross axis extend
|
|
||||||
/// show no more item at trailing when children are not full of viewport
|
|
||||||
/// if children is full of viewport, it's the same as fullCrossAxisExtend
|
|
||||||
// LastChildLayoutType.foot,
|
|
||||||
lastChildLayoutTypeBuilder: (index) =>
|
|
||||||
index == list.length
|
|
||||||
? LastChildLayoutType.foot
|
|
||||||
: LastChildLayoutType.none,
|
|
||||||
children: [
|
|
||||||
for (var i in list) DynamicPanel(item: i),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return HttpError(
|
|
||||||
errMsg: data['msg'],
|
|
||||||
fn: () {
|
|
||||||
setState(() {
|
|
||||||
_futureBuilderFuture =
|
|
||||||
_dynamicsController.queryFollowDynamic();
|
|
||||||
_futureBuilderFutureUp =
|
|
||||||
_dynamicsController.queryFollowUp();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 骨架屏
|
|
||||||
return skeleton();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 40))
|
drawer: upPanelPosition == UpPanelPosition.leftDrawer ?
|
||||||
],
|
upPanelPart(): null,
|
||||||
),
|
drawerEnableOpenDragGesture: true,
|
||||||
),
|
endDrawer: upPanelPosition == UpPanelPosition.rightDrawer ?
|
||||||
);
|
upPanelPart(): null,
|
||||||
}
|
endDrawerEnableOpenDragGesture: true,
|
||||||
|
body: Row(children: [
|
||||||
Widget skeleton() {
|
if (upPanelPosition == UpPanelPosition.leftFixed)
|
||||||
return SliverList(
|
upPanelPart(),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
Expanded(
|
||||||
return const DynamicCardSkeleton();
|
child: TabBarView(
|
||||||
}, childCount: 5),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
);
|
controller: _dynamicsController.tabController,
|
||||||
|
children: _dynamicsController.tabsPageList,
|
||||||
|
)),
|
||||||
|
if (upPanelPosition == UpPanelPosition.rightFixed)
|
||||||
|
upPanelPart(),
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
|||||||
import 'package:PiliPalaX/http/user.dart';
|
import 'package:PiliPalaX/http/user.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
import '../../../http/constants.dart';
|
||||||
|
import '../controller.dart';
|
||||||
|
|
||||||
class AuthorPanel extends StatelessWidget {
|
class AuthorPanel extends StatelessWidget {
|
||||||
final dynamic item;
|
final dynamic item;
|
||||||
const AuthorPanel({super.key, required this.item});
|
final Function? addBannedList;
|
||||||
|
const AuthorPanel({super.key, required this.item, this.addBannedList});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -77,7 +82,6 @@ class AuthorPanel extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (item.type == 'DYNAMIC_TYPE_AV')
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
@@ -132,6 +136,7 @@ class MorePanel extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (item.type == 'DYNAMIC_TYPE_AV')
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
try {
|
try {
|
||||||
@@ -151,6 +156,37 @@ class MorePanel extends StatelessWidget {
|
|||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
'分享动态',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
leading: const Icon(Icons.share_outlined, size: 19),
|
||||||
|
onTap: () async {
|
||||||
|
var result = await Share.share(
|
||||||
|
'${HttpString.baseUrl}/dynamic/${item.idStr} UP主: ${item.modules.moduleAuthor.name}')
|
||||||
|
.whenComplete(() {});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
'临时屏蔽:${item.modules.moduleAuthor.name}',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
leading: const Icon(Icons.visibility_off_outlined, size: 19),
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
DynamicsController dynamicsController =
|
||||||
|
Get.find<DynamicsController>();
|
||||||
|
dynamicsController.tempBannedList
|
||||||
|
.add(item.modules.moduleAuthor.mid);
|
||||||
|
SmartDialog.showToast(
|
||||||
|
'已临时屏蔽${item.modules.moduleAuthor.name}(${item.modules.moduleAuthor.mid}),重启恢复');
|
||||||
|
},
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
),
|
||||||
const Divider(thickness: 0.1, height: 1),
|
const Divider(thickness: 0.1, height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => Get.back(),
|
onTap: () => Get.back(),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPalaX/models/dynamics/result.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPalaX/pages/dynamics/index.dart';
|
import 'package:PiliPalaX/pages/dynamics/index.dart';
|
||||||
@@ -9,7 +10,7 @@ import 'forward_panel.dart';
|
|||||||
class DynamicPanel extends StatelessWidget {
|
class DynamicPanel extends StatelessWidget {
|
||||||
final dynamic item;
|
final dynamic item;
|
||||||
final String? source;
|
final String? source;
|
||||||
DynamicPanel({this.item, this.source, Key? key}) : super(key: key);
|
DynamicPanel({required this.item, this.source, Key? key}) : super(key: key);
|
||||||
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -18,19 +19,20 @@ class DynamicPanel extends StatelessWidget {
|
|||||||
padding: source == 'detail'
|
padding: source == 'detail'
|
||||||
? const EdgeInsets.only(bottom: 12)
|
? const EdgeInsets.only(bottom: 12)
|
||||||
: EdgeInsets.zero,
|
: EdgeInsets.zero,
|
||||||
decoration: BoxDecoration(
|
// decoration: BoxDecoration(
|
||||||
border: Border(
|
// border: Border(
|
||||||
bottom: BorderSide(
|
// bottom: BorderSide(
|
||||||
width: 8,
|
// width: 8,
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.05),
|
// color: Theme.of(context).dividerColor.withOpacity(0.05),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
|
color: Theme.of(context).cardColor.withOpacity(0.5),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(0),
|
borderRadius: BorderRadius.circular(5),
|
||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => _dynamicsController.pushDetail(item, 1),
|
onTap: () => _dynamicsController.pushDetail(item, 1),
|
||||||
@@ -38,7 +40,7 @@ class DynamicPanel extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
|
||||||
child: AuthorPanel(item: item),
|
child: AuthorPanel(item: item),
|
||||||
),
|
),
|
||||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||||
|
|||||||
@@ -11,26 +11,27 @@ import 'package:PiliPalaX/utils/utils.dart';
|
|||||||
|
|
||||||
class UpPanel extends StatefulWidget {
|
class UpPanel extends StatefulWidget {
|
||||||
final FollowUpModel? upData;
|
final FollowUpModel? upData;
|
||||||
const UpPanel(this.upData, {Key? key}) : super(key: key);
|
final ScrollController scrollController;
|
||||||
|
const UpPanel(this.upData, this.scrollController, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<UpPanel> createState() => _UpPanelState();
|
State<UpPanel> createState() => _UpPanelState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UpPanelState extends State<UpPanel> {
|
class _UpPanelState extends State<UpPanel> {
|
||||||
final ScrollController scrollController = ScrollController();
|
|
||||||
int currentMid = -1;
|
int currentMid = -1;
|
||||||
late double contentWidth = 56;
|
|
||||||
List<UpItem> upList = [];
|
List<UpItem> upList = [];
|
||||||
List<LiveUserItem> liveList = [];
|
List<LiveUserItem> liveList = [];
|
||||||
static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0);
|
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
|
bool _showLiveItems = false;
|
||||||
|
late DynamicsController dynamicsController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
|
dynamicsController = Get.find<DynamicsController>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -39,68 +40,63 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
if (widget.upData!.liveUsers != null) {
|
if (widget.upData!.liveUsers != null) {
|
||||||
liveList = widget.upData!.liveUsers!.items!;
|
liveList = widget.upData!.liveUsers!.items!;
|
||||||
}
|
}
|
||||||
return SliverPersistentHeader(
|
// return const SizedBox();
|
||||||
floating: true,
|
return CustomScrollView(
|
||||||
pinned: false,
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
delegate: _SliverHeaderDelegate(
|
controller: widget.scrollController,
|
||||||
height: 126,
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 45,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(const EdgeInsets.only()),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Container(
|
const Spacer(),
|
||||||
color: Theme.of(context).colorScheme.background,
|
const SizedBox(height: 12),
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
Text(
|
||||||
child: Row(
|
'Live(${liveList.length})',
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
style: const TextStyle(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
fontSize: 13,
|
||||||
children: [
|
|
||||||
const Text('最新关注'),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
feedBack();
|
|
||||||
Get.toNamed('/follow?mid=${userInfo.mid}');
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.only(top: 5, bottom: 5),
|
|
||||||
child: Text(
|
|
||||||
'查看全部',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
semanticsLabel:
|
||||||
|
'${_showLiveItems ? '展开' : '收起'}直播中的${liveList.length}个Up',
|
||||||
),
|
),
|
||||||
|
Icon(_showLiveItems ? Icons.expand_less : Icons.expand_more,
|
||||||
|
size: 12),
|
||||||
|
const Spacer(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_showLiveItems = !_showLiveItems;
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Container(
|
),
|
||||||
height: 90,
|
),
|
||||||
color: Theme.of(context).colorScheme.background,
|
const SliverToBoxAdapter(
|
||||||
child: Row(
|
child: SizedBox(
|
||||||
children: [
|
height: 10,
|
||||||
Expanded(
|
),
|
||||||
child: ListView(
|
),
|
||||||
physics: const BouncingScrollPhysics(),
|
SliverGrid(
|
||||||
scrollDirection: Axis.horizontal,
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
controller: scrollController,
|
crossAxisCount: 1,
|
||||||
children: [
|
mainAxisExtent: 76,
|
||||||
const SizedBox(width: 10),
|
crossAxisSpacing: 0,
|
||||||
if (liveList.isNotEmpty) ...[
|
mainAxisSpacing: 0,
|
||||||
|
),
|
||||||
|
delegate: SliverChildListDelegate(
|
||||||
|
[
|
||||||
|
if (_showLiveItems && liveList.isNotEmpty) ...[
|
||||||
for (int i = 0; i < liveList.length; i++) ...[
|
for (int i = 0; i < liveList.length; i++) ...[
|
||||||
upItemBuild(liveList[i], i)
|
upItemBuild(liveList[i], i)
|
||||||
],
|
],
|
||||||
VerticalDivider(
|
|
||||||
indent: 20,
|
|
||||||
endIndent: 40,
|
|
||||||
width: 26,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
upItemBuild(
|
upItemBuild(UpItem(face: '', uname: '全部动态', mid: -1), 0),
|
||||||
UpItem(face: '', uname: '全部动态', mid: -1), 0),
|
|
||||||
upItemBuild(
|
upItemBuild(
|
||||||
UpItem(
|
UpItem(
|
||||||
face: userInfo.face,
|
face: userInfo.face,
|
||||||
@@ -111,23 +107,14 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
for (int i = 0; i < upList.length; i++) ...[
|
for (int i = 0; i < upList.length; i++) ...[
|
||||||
upItemBuild(upList[i], i + 2)
|
upItemBuild(upList[i], i + 2)
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 6,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onInverseSurface
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
);
|
const SliverToBoxAdapter(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget upItemBuild(data, i) {
|
Widget upItemBuild(data, i) {
|
||||||
@@ -137,28 +124,27 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
feedBack();
|
feedBack();
|
||||||
if (data.type == 'up') {
|
if (data.type == 'up') {
|
||||||
currentMid = data.mid;
|
currentMid = data.mid;
|
||||||
Get.find<DynamicsController>().mid.value = data.mid;
|
// dynamicsController.mid.value = data.mid;
|
||||||
Get.find<DynamicsController>().upInfo.value = data;
|
dynamicsController.upInfo.value = data;
|
||||||
Get.find<DynamicsController>().onSelectUp(data.mid);
|
dynamicsController.onSelectUp(data.mid);
|
||||||
int liveLen = liveList.length;
|
// int liveLen = liveList.length;
|
||||||
int upLen = upList.length;
|
// int upLen = upList.length;
|
||||||
double itemWidth = contentWidth + itemPadding.horizontal;
|
// double itemWidth = contentWidth + itemPadding.horizontal;
|
||||||
double screenWidth = MediaQuery.sizeOf(context).width;
|
// double screenWidth = MediaQuery.sizeOf(context).width;
|
||||||
double moveDistance = 0.0;
|
// double moveDistance = 0.0;
|
||||||
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
// if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
||||||
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
// } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
||||||
moveDistance =
|
// moveDistance =
|
||||||
(i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
// (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
||||||
} else {
|
// } else {
|
||||||
moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
// moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
||||||
}
|
// }
|
||||||
data.hasUpdate = false;
|
data.hasUpdate = false;
|
||||||
scrollController.animateTo(
|
// scrollController.animateTo(
|
||||||
moveDistance,
|
// moveDistance,
|
||||||
duration: const Duration(milliseconds: 500),
|
// duration: const Duration(milliseconds: 500),
|
||||||
curve: Curves.easeInOut,
|
// curve: Curves.easeInOut,
|
||||||
);
|
// );
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
} else if (data.type == 'live') {
|
} else if (data.type == 'live') {
|
||||||
LiveItemModel liveItem = LiveItemModel.fromJson({
|
LiveItemModel liveItem = LiveItemModel.fromJson({
|
||||||
@@ -182,10 +168,8 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
Get.toNamed('/member?mid=${data.mid}',
|
Get.toNamed('/member?mid=${data.mid}',
|
||||||
arguments: {'face': data.face, 'heroTag': heroTag});
|
arguments: {'face': data.face, 'heroTag': heroTag});
|
||||||
},
|
},
|
||||||
child: Padding(
|
|
||||||
padding: itemPadding,
|
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
opacity: isCurrent ? 1 : 0.3,
|
opacity: isCurrent ? 1 : 0.6,
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -203,44 +187,42 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
isLabelVisible: data.type == 'live' ||
|
isLabelVisible: data.type == 'live' ||
|
||||||
(data.type == 'up' && (data.hasUpdate ?? false)),
|
(data.type == 'up' && (data.hasUpdate ?? false)),
|
||||||
backgroundColor: data.type == 'live'
|
backgroundColor: data.type == 'live'
|
||||||
? Theme.of(context).colorScheme.secondaryContainer
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondaryContainer
|
||||||
|
.withOpacity(0.7)
|
||||||
: Theme.of(context).colorScheme.primary,
|
: Theme.of(context).colorScheme.primary,
|
||||||
child: data.face != ''
|
child: data.face != ''
|
||||||
? NetworkImgLayer(
|
? NetworkImgLayer(
|
||||||
width: 50,
|
width: 38,
|
||||||
height: 50,
|
height: 38,
|
||||||
src: data.face,
|
src: data.face,
|
||||||
type: 'avatar',
|
type: 'avatar',
|
||||||
)
|
)
|
||||||
: const CircleAvatar(
|
: const CircleAvatar(
|
||||||
radius: 25,
|
radius: 19,
|
||||||
backgroundImage: AssetImage(
|
backgroundImage: AssetImage(
|
||||||
'assets/images/noface.jpeg',
|
'assets/images/logo/logo_android_2.png',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
const SizedBox(height: 3),
|
||||||
padding: const EdgeInsets.only(top: 4),
|
Text(
|
||||||
child: SizedBox(
|
|
||||||
width: contentWidth,
|
|
||||||
child: Text(
|
|
||||||
data.uname,
|
data.uname,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.clip,
|
||||||
softWrap: false,
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: currentMid == data.mid
|
color: currentMid == data.mid
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context).colorScheme.outline,
|
: Theme.of(context).colorScheme.outline,
|
||||||
fontSize:
|
height: 1.1,
|
||||||
Theme.of(context).textTheme.labelMedium!.fontSize),
|
fontSize: 12.5),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,14 +255,7 @@ class UpPanelSkeleton extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView.builder(
|
return Column(
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: 10,
|
|
||||||
itemBuilder: ((context, index) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -299,9 +274,6 @@ class UpPanelSkeleton extends StatelessWidget {
|
|||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
|||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 80,
|
height: 70,
|
||||||
padding: const EdgeInsets.fromLTRB(12, 0, 10, 10),
|
padding: const EdgeInsets.fromLTRB(10, 0, 8, 8),
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: const LinearGradient(
|
||||||
@@ -139,17 +139,17 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
|||||||
'时长${Utils.durationReadFormat(content.durationText)}',
|
'时长${Utils.durationReadFormat(content.durationText)}',
|
||||||
),
|
),
|
||||||
if (content.durationText != null)
|
if (content.durationText != null)
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 6),
|
||||||
Text(content.stat.play + '次围观'),
|
Text(content.stat.play + '次围观'),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 6),
|
||||||
Text(content.stat.danmu + '条弹幕')
|
Text(content.stat.danmu + '条弹幕')
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/play.png',
|
'assets/images/play.png',
|
||||||
width: 60,
|
width: 50,
|
||||||
height: 60,
|
height: 50,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class FansController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future queryFans(type) async {
|
Future queryFans(type) async {
|
||||||
if (type == 'init') {
|
if (type == 'init' || type == 'refresh') {
|
||||||
pn = 1;
|
pn = 1;
|
||||||
loadingText.value == '加载中...';
|
loadingText.value == '加载中...';
|
||||||
}
|
}
|
||||||
@@ -49,11 +49,14 @@ class FansController extends GetxController {
|
|||||||
} else if (type == 'onLoad') {
|
} else if (type == 'onLoad') {
|
||||||
fansList.addAll(res['data'].list);
|
fansList.addAll(res['data'].list);
|
||||||
}
|
}
|
||||||
print(total);
|
print('fansList: ${fansList.length}, total: $total');
|
||||||
if ((pn == 1 && total < ps) || res['data'].list.isEmpty) {
|
if ((pn == 1 && total < ps) || res['data'].list.isEmpty) {
|
||||||
loadingText.value = '没有更多了';
|
loadingText.value = '没有更多了';
|
||||||
}
|
}
|
||||||
pn += 1;
|
pn += 1;
|
||||||
|
if (total > ps && pn == 2) {
|
||||||
|
queryFans('onLoad');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:PiliPalaX/common/widgets/http_error.dart';
|
|||||||
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
||||||
import 'package:PiliPalaX/models/fans/result.dart';
|
import 'package:PiliPalaX/models/fans/result.dart';
|
||||||
|
|
||||||
|
import '../../common/constants.dart';
|
||||||
|
import '../../utils/grid.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/fan_item.dart';
|
import 'widgets/fan_item.dart';
|
||||||
|
|
||||||
@@ -60,48 +62,36 @@ class _FansPageState extends State<FansPage> {
|
|||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
onRefresh: () async => await _fansController.queryFans('init'),
|
onRefresh: () async => await _fansController.queryFans('init'),
|
||||||
child: FutureBuilder(
|
child: CustomScrollView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
controller: scrollController,
|
||||||
|
slivers: [
|
||||||
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.data != null) {
|
||||||
var data = snapshot.data;
|
var data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
|
return Obx(() {
|
||||||
List<FansItemModel> list = _fansController.fansList;
|
List<FansItemModel> list = _fansController.fansList;
|
||||||
return Obx(
|
return list.isNotEmpty
|
||||||
() => list.isNotEmpty
|
? SliverGrid(
|
||||||
? ListView.builder(
|
gridDelegate:
|
||||||
controller: scrollController,
|
SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
itemCount: list.length + 1,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
if (index == list.length) {
|
maxCrossAxisExtent:
|
||||||
return Container(
|
Grid.maxRowWidth * 2,
|
||||||
height:
|
mainAxisExtent: 56),
|
||||||
MediaQuery.of(context).padding.bottom + 60,
|
delegate: SliverChildBuilderDelegate(
|
||||||
padding: EdgeInsets.only(
|
(BuildContext context, int index) {
|
||||||
bottom:
|
|
||||||
MediaQuery.of(context).padding.bottom),
|
|
||||||
child: Center(
|
|
||||||
child: Obx(
|
|
||||||
() => Text(
|
|
||||||
_fansController.loadingText.value,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline,
|
|
||||||
fontSize: 13),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return fanItem(item: list[index]);
|
return fanItem(item: list[index]);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
childCount: list.length,
|
||||||
: const CustomScrollView(
|
))
|
||||||
slivers: [NoData()],
|
: const NoData();
|
||||||
),
|
});
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
@@ -110,11 +100,19 @@ class _FansPageState extends State<FansPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return const SizedBox();
|
return const SliverToBoxAdapter(
|
||||||
}
|
child: SizedBox(
|
||||||
},
|
height: 200,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'package:PiliPalaX/common/skeleton/video_card_h.dart';
|
||||||
|
import 'package:PiliPalaX/common/skeleton/video_card_v.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -68,17 +70,12 @@ class _FavPageState extends State<FavPage> {
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
context,
|
mainAxisExtent: 0),
|
||||||
Grid.maxRowWidth * 2,
|
|
||||||
StyleString.safeSpace) /
|
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount:
|
childCount:
|
||||||
_favController.favFolderData.value.list!.length,
|
_favController.favFolderData.value.list!.length,
|
||||||
@@ -103,7 +100,25 @@ class _FavPageState extends State<FavPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return const Text('请求中');
|
return CustomScrollView(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
slivers: [
|
||||||
|
SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return const VideoCardHSkeleton();
|
||||||
|
},
|
||||||
|
childCount: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -215,16 +215,13 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
? const SliverToBoxAdapter(child: SizedBox())
|
? const SliverToBoxAdapter(child: SizedBox())
|
||||||
: SliverGrid(
|
: SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate:
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio:
|
||||||
context,
|
StyleString.aspectRatio * 2.3,
|
||||||
Grid.maxRowWidth * 2,
|
mainAxisExtent: 0),
|
||||||
StyleString.safeSpace) /
|
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate:
|
delegate:
|
||||||
SliverChildBuilderDelegate((context, index) {
|
SliverChildBuilderDelegate((context, index) {
|
||||||
return FavVideoCardH(
|
return FavVideoCardH(
|
||||||
@@ -244,7 +241,13 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 10),
|
}, childCount: 10),
|
||||||
|
|||||||
@@ -51,18 +51,17 @@ class HistoryController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 暂停观看历史
|
// 暂停观看历史
|
||||||
Future onPauseHistory() async {
|
Future onPauseHistory(BuildContext context) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content:
|
content:
|
||||||
Text(!pauseStatus.value ? '啊叻?你要暂停历史记录功能吗?' : '啊叻?要恢复历史记录功能吗?'),
|
Text(!pauseStatus.value ? '啊叻?你要暂停历史记录功能吗?' : '啊叻?要恢复历史记录功能吗?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: const Text('取消')),
|
child: const Text('取消')),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -75,7 +74,7 @@ class HistoryController extends GetxController {
|
|||||||
pauseStatus.value = !pauseStatus.value;
|
pauseStatus.value = !pauseStatus.value;
|
||||||
localCache.put(LocalCacheKey.historyPause, pauseStatus.value);
|
localCache.put(LocalCacheKey.historyPause, pauseStatus.value);
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: Text(!pauseStatus.value ? '确认暂停' : '确认恢复'),
|
child: Text(!pauseStatus.value ? '确认暂停' : '确认恢复'),
|
||||||
)
|
)
|
||||||
@@ -97,17 +96,16 @@ class HistoryController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 清空观看历史
|
// 清空观看历史
|
||||||
Future onClearHistory() async {
|
Future onClearHistory(BuildContext context) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: const Text('啊叻?你要清空历史记录功能吗?'),
|
content: const Text('啊叻?你要清空历史记录功能吗?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: const Text('取消')),
|
child: const Text('取消')),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -117,7 +115,7 @@ class HistoryController extends GetxController {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
SmartDialog.showToast('清空观看历史');
|
SmartDialog.showToast('清空观看历史');
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
historyList.clear();
|
historyList.clear();
|
||||||
},
|
},
|
||||||
child: const Text('确认清空'),
|
child: const Text('确认清空'),
|
||||||
@@ -158,17 +156,16 @@ class HistoryController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 删除选中的记录
|
// 删除选中的记录
|
||||||
Future onDelCheckedHistory() async {
|
Future onDelCheckedHistory(BuildContext context) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: const Text('确认删除所选历史记录吗?'),
|
content: const Text('确认删除所选历史记录吗?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -179,7 +176,7 @@ class HistoryController extends GetxController {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
/// TODO 优化
|
/// TODO 优化
|
||||||
await SmartDialog.dismiss();
|
Get.back();
|
||||||
SmartDialog.showLoading(msg: '请求中');
|
SmartDialog.showLoading(msg: '请求中');
|
||||||
List<HisListItem> result =
|
List<HisListItem> result =
|
||||||
historyList.where((e) => e.checked!).toList();
|
historyList.where((e) => e.checked!).toList();
|
||||||
|
|||||||
@@ -87,10 +87,10 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
// 处理菜单项选择的逻辑
|
// 处理菜单项选择的逻辑
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'pause':
|
case 'pause':
|
||||||
_historyController.onPauseHistory();
|
_historyController.onPauseHistory(context);
|
||||||
break;
|
break;
|
||||||
case 'clear':
|
case 'clear':
|
||||||
_historyController.onClearHistory();
|
_historyController.onClearHistory(context);
|
||||||
break;
|
break;
|
||||||
case 'del':
|
case 'del':
|
||||||
_historyController.onDelHistory();
|
_historyController.onDelHistory();
|
||||||
@@ -162,7 +162,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
child: const Text('全选'),
|
child: const Text('全选'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _historyController.onDelCheckedHistory(),
|
onPressed: () => _historyController.onDelCheckedHistory(context),
|
||||||
child: Text(
|
child: Text(
|
||||||
'删除',
|
'删除',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||||
@@ -194,16 +194,13 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
() => _historyController.historyList.isNotEmpty
|
() => _historyController.historyList.isNotEmpty
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate:
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio:
|
||||||
context,
|
StyleString.aspectRatio * 2.3,
|
||||||
Grid.maxRowWidth * 2,
|
mainAxisExtent: 0),
|
||||||
StyleString.safeSpace) /
|
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return HistoryItem(
|
return HistoryItem(
|
||||||
@@ -231,7 +228,13 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 10),
|
}, childCount: 10),
|
||||||
|
|||||||
@@ -86,14 +86,12 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
|
|||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
Obx(() => SliverGrid(
|
Obx(() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(context,
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
Grid.maxRowWidth * 2, StyleString.safeSpace) /
|
mainAxisExtent: 0),
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return HistoryItem(
|
return HistoryItem(
|
||||||
|
|||||||
@@ -67,8 +67,11 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
|
|
||||||
void setTabConfig() async {
|
void setTabConfig() async {
|
||||||
defaultTabs = [...tabsConfig];
|
defaultTabs = [...tabsConfig];
|
||||||
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
tabbarSort = settingStorage
|
||||||
defaultValue: ['live', 'rcmd', 'hot', 'bangumi']).map<String>((i) => i.toString()).toList();
|
.get(SettingBoxKey.tabbarSort,
|
||||||
|
defaultValue: ['live', 'rcmd', 'hot', 'rank', 'bangumi'])
|
||||||
|
.map<String>((i) => i.toString())
|
||||||
|
.toList();
|
||||||
defaultTabs.retainWhere(
|
defaultTabs.retainWhere(
|
||||||
(item) => tabbarSort.contains((item['type'] as TabType).id));
|
(item) => tabbarSort.contains((item['type'] as TabType).id));
|
||||||
defaultTabs.sort((a, b) => tabbarSort
|
defaultTabs.sort((a, b) => tabbarSort
|
||||||
|
|||||||
@@ -46,54 +46,31 @@ class _HomePageState extends State<HomePage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
|
// Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
|
||||||
// 设置状态栏图标的亮度
|
// // 设置状态栏图标的亮度
|
||||||
if (_homeController.enableGradientBg) {
|
// if (_homeController.enableGradientBg) {
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
// SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: currentBrightness == Brightness.light
|
// statusBarIconBrightness: currentBrightness == Brightness.light
|
||||||
? Brightness.dark
|
// ? Brightness.dark
|
||||||
: Brightness.light,
|
// : Brightness.light,
|
||||||
));
|
// ));
|
||||||
}
|
// }
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
appBar: _homeController.enableGradientBg
|
backgroundColor: Colors.transparent,
|
||||||
? null
|
appBar: AppBar(
|
||||||
: AppBar(toolbarHeight: 0, elevation: 0),
|
toolbarHeight: 0,
|
||||||
body: Stack(
|
elevation: 0,
|
||||||
children: [
|
backgroundColor: Colors.transparent,
|
||||||
// gradient background
|
systemOverlayStyle: SystemUiOverlayStyle(
|
||||||
if (_homeController.enableGradientBg) ...[
|
statusBarIconBrightness:
|
||||||
Align(
|
MediaQuery.of(context).platformBrightness == Brightness.dark
|
||||||
alignment: Alignment.topLeft,
|
? Brightness.light
|
||||||
child: Opacity(
|
: Brightness.dark,
|
||||||
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]),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
body: Column(
|
||||||
),
|
|
||||||
],
|
|
||||||
Column(
|
|
||||||
children: [
|
children: [
|
||||||
CustomAppBar(
|
CustomAppBar(
|
||||||
stream: _homeController.hideSearchBar
|
stream: _homeController.hideSearchBar
|
||||||
@@ -115,8 +92,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
child: TabBar(
|
child: TabBar(
|
||||||
controller: _homeController.tabController,
|
controller: _homeController.tabController,
|
||||||
tabs: [
|
tabs: [
|
||||||
for (var i in _homeController.tabs)
|
for (var i in _homeController.tabs) Tab(text: i['label'])
|
||||||
Tab(text: i['label'])
|
|
||||||
],
|
],
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
@@ -145,8 +121,6 @@ class _HomePageState extends State<HomePage>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class HotController extends GetxController {
|
|||||||
videoList.addAll(res['data']);
|
videoList.addAll(res['data']);
|
||||||
}
|
}
|
||||||
_currentPage += 1;
|
_currentPage += 1;
|
||||||
|
if (_currentPage == 2) queryHotFeed('onLoad');
|
||||||
}
|
}
|
||||||
isLoadingMore = false;
|
isLoadingMore = false;
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
slivers: [
|
slivers: [
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
// 单列布局 EdgeInsets.zero
|
// 单列布局 EdgeInsets.zero
|
||||||
padding:
|
padding: const EdgeInsets.fromLTRB(
|
||||||
const EdgeInsets.fromLTRB(0, StyleString.safeSpace - 5, 0, 0),
|
StyleString.safeSpace, StyleString.safeSpace - 5, 0, 0),
|
||||||
sliver: FutureBuilder(
|
sliver: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@@ -92,19 +92,12 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverGrid(
|
() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
// 行间距
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
// 列间距
|
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
|
||||||
// 最大宽度
|
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
context,
|
mainAxisExtent: 0),
|
||||||
Grid.maxRowWidth * 2,
|
|
||||||
StyleString.safeSpace) /
|
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: _hotController.videoList[index],
|
videoItem: _hotController.videoList[index],
|
||||||
@@ -135,7 +128,12 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 10),
|
}, childCount: 10),
|
||||||
|
|||||||
@@ -23,18 +23,17 @@ class LaterController extends GetxController {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future toViewDel({int? aid}) async {
|
Future toViewDel(BuildContext context, {int? aid}) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(
|
content: Text(
|
||||||
aid != null ? '即将移除该视频,确定是否移除' : '即将删除所有已观看视频,此操作不可恢复。确定是否删除?'),
|
aid != null ? '即将移除该视频,确定是否移除' : '即将删除所有已观看视频,此操作不可恢复。确定是否删除?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -51,7 +50,7 @@ class LaterController extends GetxController {
|
|||||||
queryLaterList();
|
queryLaterList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
},
|
},
|
||||||
child: Text(aid != null ? '确认移除' : '确认删除'),
|
child: Text(aid != null ? '确认移除' : '确认删除'),
|
||||||
@@ -63,17 +62,16 @@ class LaterController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 一键清空
|
// 一键清空
|
||||||
Future toViewClear() async {
|
Future toViewClear(BuildContext context) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('清空确认'),
|
title: const Text('清空确认'),
|
||||||
content: const Text('确定要清空你的稍后再看列表吗?'),
|
content: const Text('确定要清空你的稍后再看列表吗?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -85,7 +83,7 @@ class LaterController extends GetxController {
|
|||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
laterList.clear();
|
laterList.clear();
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
Obx(
|
Obx(
|
||||||
() => _laterController.laterList.isNotEmpty
|
() => _laterController.laterList.isNotEmpty
|
||||||
? TextButton(
|
? TextButton(
|
||||||
onPressed: () => _laterController.toViewDel(),
|
onPressed: () => _laterController.toViewDel(context),
|
||||||
child: const Text('移除已看'),
|
child: const Text('移除已看'),
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
@@ -56,7 +56,7 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
() => _laterController.laterList.isNotEmpty
|
() => _laterController.laterList.isNotEmpty
|
||||||
? IconButton(
|
? IconButton(
|
||||||
tooltip: '一键清空',
|
tooltip: '一键清空',
|
||||||
onPressed: () => _laterController.toViewClear(),
|
onPressed: () => _laterController.toViewClear(context),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear_all_outlined,
|
Icons.clear_all_outlined,
|
||||||
size: 21,
|
size: 21,
|
||||||
@@ -72,7 +72,10 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
controller: _laterController.scrollController,
|
controller: _laterController.scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
FutureBuilder(
|
SliverPadding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
||||||
|
sliver: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
@@ -83,25 +86,27 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
!_laterController.isLoading.value
|
!_laterController.isLoading.value
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate:
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent:
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
|
||||||
context,
|
|
||||||
Grid.maxRowWidth * 2,
|
Grid.maxRowWidth * 2,
|
||||||
StyleString.safeSpace) /
|
childAspectRatio:
|
||||||
2.1 /
|
StyleString.aspectRatio * 2.3,
|
||||||
StyleString.aspectRatio),
|
mainAxisExtent: 0),
|
||||||
delegate:
|
delegate: SliverChildBuilderDelegate(
|
||||||
SliverChildBuilderDelegate((context, index) {
|
(context, index) {
|
||||||
var videoItem = _laterController.laterList[index];
|
var videoItem =
|
||||||
|
_laterController.laterList[index];
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
source: 'later',
|
source: 'later',
|
||||||
longPress: () => _laterController.toViewDel(
|
longPress: () =>
|
||||||
|
_laterController.toViewDel(context,
|
||||||
aid: videoItem.aid));
|
aid: videoItem.aid));
|
||||||
}, childCount: _laterController.laterList.length),
|
},
|
||||||
|
childCount:
|
||||||
|
_laterController.laterList.length),
|
||||||
)
|
)
|
||||||
: _laterController.isLoading.value
|
: _laterController.isLoading.value
|
||||||
? const SliverToBoxAdapter(
|
? const SliverToBoxAdapter(
|
||||||
@@ -113,20 +118,27 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => setState(() {
|
fn: () => setState(() {
|
||||||
_futureBuilderFuture = _laterController.queryLaterList();
|
_futureBuilderFuture =
|
||||||
|
_laterController.queryLaterList();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 10),
|
}, childCount: 10),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
)),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: MediaQuery.of(context).padding.bottom + 10,
|
height: MediaQuery.of(context).padding.bottom + 10,
|
||||||
|
|||||||
@@ -141,15 +141,12 @@ class _LivePageState extends State<LivePage>
|
|||||||
|
|
||||||
Widget contentGrid(ctr, liveList) {
|
Widget contentGrid(ctr, liveList) {
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
// 行间距
|
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
// 列间距
|
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
// 最大宽度
|
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth,
|
maxCrossAxisExtent: Grid.maxRowWidth,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth, StyleString.safeSpace) / StyleString.aspectRatio+
|
childAspectRatio: StyleString.aspectRatio,
|
||||||
MediaQuery.textScalerOf(context).scale(80),
|
mainAxisExtent: MediaQuery.textScalerOf(context).scale(80),
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ class LoginPageController extends GetxController {
|
|||||||
Future getCaptcha(oncall) async {
|
Future getCaptcha(oncall) async {
|
||||||
SmartDialog.showLoading(msg: '请求中...');
|
SmartDialog.showLoading(msg: '请求中...');
|
||||||
var result = await LoginHttp.queryCaptcha();
|
var result = await LoginHttp.queryCaptcha();
|
||||||
|
SmartDialog.dismiss();
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
CaptchaDataModel captchaData = result['data'];
|
CaptchaDataModel captchaData = result['data'];
|
||||||
var registerData = Gt3RegisterData(
|
var registerData = Gt3RegisterData(
|
||||||
@@ -119,7 +120,6 @@ class LoginPageController extends GetxController {
|
|||||||
success: true,
|
success: true,
|
||||||
);
|
);
|
||||||
captcha.addEventHandler(onShow: (Map<String, dynamic> message) async {
|
captcha.addEventHandler(onShow: (Map<String, dynamic> message) async {
|
||||||
SmartDialog.dismiss();
|
|
||||||
}, onClose: (Map<String, dynamic> message) async {
|
}, onClose: (Map<String, dynamic> message) async {
|
||||||
SmartDialog.showToast('关闭验证');
|
SmartDialog.showToast('关闭验证');
|
||||||
}, onResult: (Map<String, dynamic> message) async {
|
}, onResult: (Map<String, dynamic> message) async {
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ import 'package:PiliPalaX/pages/media/index.dart';
|
|||||||
import 'package:PiliPalaX/pages/rank/index.dart';
|
import 'package:PiliPalaX/pages/rank/index.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
import '../../models/common/dynamic_badge_mode.dart';
|
import '../../models/common/dynamic_badge_mode.dart';
|
||||||
import '../../models/common/nav_bar_config.dart';
|
import '../../models/common/nav_bar_config.dart';
|
||||||
|
|
||||||
class MainController extends GetxController {
|
class MainController extends GetxController {
|
||||||
List<Widget> pages = <Widget>[
|
List<Widget> pages = <Widget>[
|
||||||
const HomePage(),
|
const HomePage(),
|
||||||
const RankPage(),
|
// const RankPage(),
|
||||||
const DynamicsPage(),
|
const DynamicsPage(),
|
||||||
const MediaPage(),
|
const MediaPage(),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:PiliPalaX/common/constants.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:PiliPalaX/models/common/dynamic_badge_mode.dart';
|
import 'package:PiliPalaX/models/common/dynamic_badge_mode.dart';
|
||||||
import 'package:PiliPalaX/pages/dynamics/index.dart';
|
import 'package:PiliPalaX/pages/dynamics/index.dart';
|
||||||
import 'package:PiliPalaX/pages/home/index.dart';
|
import 'package:PiliPalaX/pages/home/index.dart';
|
||||||
import 'package:PiliPalaX/pages/media/index.dart';
|
import 'package:PiliPalaX/pages/media/index.dart';
|
||||||
import 'package:PiliPalaX/pages/rank/index.dart';
|
|
||||||
import 'package:PiliPalaX/utils/event_bus.dart';
|
import 'package:PiliPalaX/utils/event_bus.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
@@ -24,7 +23,6 @@ class MainApp extends StatefulWidget {
|
|||||||
class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||||
final MainController _mainController = Get.put(MainController());
|
final MainController _mainController = Get.put(MainController());
|
||||||
final HomeController _homeController = Get.put(HomeController());
|
final HomeController _homeController = Get.put(HomeController());
|
||||||
final RankController _rankController = Get.put(RankController());
|
|
||||||
final DynamicsController _dynamicController = Get.put(DynamicsController());
|
final DynamicsController _dynamicController = Get.put(DynamicsController());
|
||||||
final MediaController _mediaController = Get.put(MediaController());
|
final MediaController _mediaController = Get.put(MediaController());
|
||||||
|
|
||||||
@@ -32,6 +30,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
late bool enableMYBar;
|
late bool enableMYBar;
|
||||||
late bool adaptiveNavBar;
|
late bool adaptiveNavBar;
|
||||||
|
late bool enableGradientBg;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -42,6 +41,8 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true);
|
enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true);
|
||||||
adaptiveNavBar =
|
adaptiveNavBar =
|
||||||
setting.get(SettingBoxKey.adaptiveNavBar, defaultValue: false);
|
setting.get(SettingBoxKey.adaptiveNavBar, defaultValue: false);
|
||||||
|
enableGradientBg = setting.get(SettingBoxKey.enableGradientBg,
|
||||||
|
defaultValue: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setIndex(int value) async {
|
void setIndex(int value) async {
|
||||||
@@ -63,20 +64,20 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
_homeController.flag = false;
|
_homeController.flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPage is RankPage) {
|
// if (currentPage is RankPage) {
|
||||||
if (_rankController.flag) {
|
// if (_rankController.flag) {
|
||||||
// 单击返回顶部 双击并刷新
|
// // 单击返回顶部 双击并刷新
|
||||||
if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) {
|
// if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) {
|
||||||
_rankController.onRefresh();
|
// _rankController.onRefresh();
|
||||||
} else {
|
// } else {
|
||||||
_rankController.animateToTop();
|
// _rankController.animateToTop();
|
||||||
}
|
// }
|
||||||
_lastSelectTime = DateTime.now().millisecondsSinceEpoch;
|
// _lastSelectTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
}
|
// }
|
||||||
_rankController.flag = true;
|
// _rankController.flag = true;
|
||||||
} else {
|
// } else {
|
||||||
_rankController.flag = false;
|
// _rankController.flag = false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (currentPage is DynamicsPage) {
|
if (currentPage is DynamicsPage) {
|
||||||
if (_dynamicController.flag) {
|
if (_dynamicController.flag) {
|
||||||
@@ -113,46 +114,85 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
onPopInvoked: (bool didPop) async {
|
onPopInvoked: (bool didPop) async {
|
||||||
_mainController.onBackPressed(context);
|
_mainController.onBackPressed(context);
|
||||||
},
|
},
|
||||||
child: adaptiveNavBar
|
child: Scaffold(
|
||||||
? AdaptiveScaffold(
|
extendBody: true,
|
||||||
body: (_) => PageView(
|
body: Stack(children: [
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
// gradient background
|
||||||
controller: _mainController.pageController,
|
if (enableGradientBg) ...[
|
||||||
onPageChanged: (index) {
|
Align(
|
||||||
_mainController.selectedIndex = index;
|
alignment: Alignment.topLeft,
|
||||||
setState(() {});
|
child: Opacity(
|
||||||
},
|
opacity: 0.6,
|
||||||
children: _mainController.pages,
|
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]),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (adaptiveNavBar) ...[
|
||||||
|
SizedBox(
|
||||||
|
width: 55,
|
||||||
|
child: NavigationRail(
|
||||||
|
groupAlignment: 0.0,
|
||||||
|
minWidth: 40.0,
|
||||||
|
backgroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surface
|
||||||
|
.withOpacity(0.2),
|
||||||
|
selectedIndex: _mainController.selectedIndex,
|
||||||
|
onDestinationSelected: (value) => setIndex(value),
|
||||||
|
labelType: NavigationRailLabelType.none,
|
||||||
destinations: _mainController.navigationBars
|
destinations: _mainController.navigationBars
|
||||||
.map((e) => NavigationDestination(
|
.map((e) => NavigationRailDestination(
|
||||||
icon: Badge(
|
icon: Badge(
|
||||||
label: _mainController.dynamicBadgeType ==
|
label: _mainController.dynamicBadgeType ==
|
||||||
DynamicBadgeMode.number
|
DynamicBadgeMode.number
|
||||||
? Text(e['count'].toString())
|
? Text(e['count'].toString())
|
||||||
: null,
|
: null,
|
||||||
padding: const EdgeInsets.fromLTRB(2, 0, 2, 0),
|
padding:
|
||||||
isLabelVisible: _mainController.dynamicBadgeType !=
|
const EdgeInsets.fromLTRB(2, 0, 2, 0),
|
||||||
|
isLabelVisible:
|
||||||
|
_mainController.dynamicBadgeType !=
|
||||||
DynamicBadgeMode.hidden &&
|
DynamicBadgeMode.hidden &&
|
||||||
e['count'] > 0,
|
e['count'] > 0,
|
||||||
child: e['icon'],
|
child: e['icon'],
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).colorScheme.primary,
|
Theme.of(context).colorScheme.primary,
|
||||||
textColor:
|
textColor: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.onInverseSurface,
|
.colorScheme
|
||||||
|
.onInverseSurface,
|
||||||
),
|
),
|
||||||
selectedIcon: e['selectIcon'],
|
selectedIcon: e['selectIcon'],
|
||||||
label: e['label'],
|
label: Text(e['label']),
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 6),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
onSelectedIndexChange: (value) => setIndex(value),
|
)),
|
||||||
selectedIndex: _mainController.selectedIndex,
|
],
|
||||||
extendedNavigationRailWidth: 180,
|
Expanded(
|
||||||
transitionDuration: const Duration(milliseconds: 500),
|
child: PageView(
|
||||||
useDrawer: true)
|
|
||||||
: Scaffold(
|
|
||||||
extendBody: true,
|
|
||||||
body: PageView(
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
controller: _mainController.pageController,
|
controller: _mainController.pageController,
|
||||||
onPageChanged: (index) {
|
onPageChanged: (index) {
|
||||||
@@ -161,7 +201,13 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
},
|
},
|
||||||
children: _mainController.pages,
|
children: _mainController.pages,
|
||||||
),
|
),
|
||||||
bottomNavigationBar: StreamBuilder(
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
bottomNavigationBar: adaptiveNavBar
|
||||||
|
? null
|
||||||
|
: StreamBuilder(
|
||||||
stream: _mainController.hideTabBar
|
stream: _mainController.hideTabBar
|
||||||
? _mainController.bottomBarStream.stream
|
? _mainController.bottomBarStream.stream
|
||||||
: StreamController<bool>.broadcast().stream,
|
: StreamController<bool>.broadcast().stream,
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
Color primary = Theme.of(context).colorScheme.primary;
|
Color primary = Theme.of(context).colorScheme.primary;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(toolbarHeight: 30),
|
backgroundColor: Colors.transparent,
|
||||||
|
appBar: AppBar(toolbarHeight: 30, backgroundColor: Colors.transparent),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
controller: mediaController.scrollController,
|
controller: mediaController.scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -74,7 +75,8 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
child: Text(
|
child: Text(
|
||||||
'媒体库',
|
'媒体库',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.titleLarge!.fontSize,
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.titleLarge!.fontSize,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -88,8 +90,7 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
Icons.settings_outlined,
|
Icons.settings_outlined,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
)
|
)),
|
||||||
),
|
|
||||||
for (var i in mediaController.list) ...[
|
for (var i in mediaController.list) ...[
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => i['onTap'](),
|
onTap: () => i['onTap'](),
|
||||||
@@ -145,14 +146,19 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
),
|
),
|
||||||
if (mediaController.favFolderData.value.count != null)
|
if (mediaController.favFolderData.value.count != null)
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: mediaController.favFolderData.value.count
|
text: "${mediaController.favFolderData.value.count} ",
|
||||||
.toString(),
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize:
|
fontSize:
|
||||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
WidgetSpan(
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 18,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -275,10 +281,16 @@ class FavFolderItem extends StatelessWidget {
|
|||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onInverseSurface
|
||||||
|
.withOpacity(0.4),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onInverseSurface
|
||||||
|
.withOpacity(0.4),
|
||||||
offset: const Offset(4, -12), // 阴影与容器的距离
|
offset: const Offset(4, -12), // 阴影与容器的距离
|
||||||
blurRadius: 0.0, // 高斯的标准偏差与盒子的形状卷积。
|
blurRadius: 0.0, // 高斯的标准偏差与盒子的形状卷积。
|
||||||
spreadRadius: 0.0, // 在应用模糊之前,框应该膨胀的量。
|
spreadRadius: 0.0, // 在应用模糊之前,框应该膨胀的量。
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class MemberController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关注/取关up
|
// 关注/取关up
|
||||||
Future actionRelationMod() async {
|
Future actionRelationMod(BuildContext context) async {
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
@@ -78,19 +78,18 @@ class MemberController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (attribute.value == 128) {
|
if (attribute.value == 128) {
|
||||||
blockUser();
|
blockUser(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
|
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'点错了',
|
'点错了',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -98,6 +97,7 @@ class MemberController extends GetxController {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
Get.back();
|
||||||
await VideoHttp.relationMod(
|
await VideoHttp.relationMod(
|
||||||
mid: mid,
|
mid: mid,
|
||||||
act: memberInfo.value.isFollowed! ? 2 : 1,
|
act: memberInfo.value.isFollowed! ? 2 : 1,
|
||||||
@@ -105,7 +105,6 @@ class MemberController extends GetxController {
|
|||||||
);
|
);
|
||||||
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
|
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
|
||||||
relationSearch();
|
relationSearch();
|
||||||
SmartDialog.dismiss();
|
|
||||||
memberInfo.update((val) {});
|
memberInfo.update((val) {});
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
@@ -146,21 +145,20 @@ class MemberController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 拉黑用户
|
// 拉黑用户
|
||||||
Future blockUser() async {
|
Future blockUser(BuildContext context) async {
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
|
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'点错了',
|
'点错了',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -168,12 +166,12 @@ class MemberController extends GetxController {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
Get.back();
|
||||||
var res = await VideoHttp.relationMod(
|
var res = await VideoHttp.relationMod(
|
||||||
mid: mid,
|
mid: mid,
|
||||||
act: attribute.value != 128 ? 5 : 6,
|
act: attribute.value != 128 ? 5 : 6,
|
||||||
reSrc: 11,
|
reSrc: 11,
|
||||||
);
|
);
|
||||||
SmartDialog.dismiss();
|
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
attribute.value = attribute.value != 128 ? 128 : 0;
|
attribute.value = attribute.value != 128 ? 128 : 0;
|
||||||
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
|
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
bool isHorizontal = context.width > context.height;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
primary: true,
|
primary: true,
|
||||||
body: Column(
|
body: Column(
|
||||||
@@ -118,7 +119,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||||
if (_memberController.ownerMid != _memberController.mid) ...[
|
if (_memberController.ownerMid != _memberController.mid) ...[
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => _memberController.blockUser(),
|
onTap: () => _memberController.blockUser(context),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -158,29 +159,42 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
profileWidget(),
|
profileWidget(isHorizontal),
|
||||||
|
Row(children: [
|
||||||
/// 动态链接
|
const Spacer(),
|
||||||
ListTile(
|
InkWell(
|
||||||
onTap: _memberController.pushDynamicsPage,
|
onTap: _memberController.pushDynamicsPage,
|
||||||
title: const Text('Ta的动态'),
|
child: const Row(
|
||||||
trailing:
|
children: [
|
||||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
Text('Ta的动态', style: TextStyle(height: 2)),
|
||||||
|
SizedBox(width: 5),
|
||||||
|
Icon(Icons.arrow_forward_ios, size: 19),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
/// 视频
|
const Spacer(),
|
||||||
ListTile(
|
InkWell(
|
||||||
onTap: _memberController.pushArchivesPage,
|
onTap: _memberController.pushArchivesPage,
|
||||||
title: const Text('Ta的投稿'),
|
child: const Row(
|
||||||
trailing:
|
children: [
|
||||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
Text('Ta的投稿', style: TextStyle(height: 2)),
|
||||||
|
SizedBox(width: 5),
|
||||||
|
Icon(Icons.arrow_forward_ios, size: 19),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
/// 专栏
|
const Spacer(),
|
||||||
ListTile(
|
InkWell(
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
title: const Text('Ta的专栏'),
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Text('Ta的专栏', style: TextStyle(height: 2)),
|
||||||
|
SizedBox(width: 5),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
]),
|
||||||
MediaQuery.removePadding(
|
MediaQuery.removePadding(
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
removeBottom: true,
|
removeBottom: true,
|
||||||
@@ -279,29 +293,64 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget profileWidget() {
|
Widget profileWidget(bool isHorizontal) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 18, right: 18, bottom: 20),
|
padding: const EdgeInsets.only(left: 18, right: 18, bottom: 20),
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.hasData) {
|
||||||
Map data = snapshot.data!;
|
Map data = snapshot.data!;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => Stack(
|
() => Stack(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
|
children: [profilePanelAndDetailInfo(isHorizontal, false)]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return profilePanelAndDetailInfo(isHorizontal, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget profilePanelAndDetailInfo(bool isHorizontal, bool loadingStatus) {
|
||||||
|
if (isHorizontal) {
|
||||||
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Expanded(
|
||||||
|
child: ProfilePanel(
|
||||||
|
ctr: _memberController, loadingStatus: loadingStatus)),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Expanded(child: profileDetailInfo()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ProfilePanel(ctr: _memberController),
|
ProfilePanel(ctr: _memberController, loadingStatus: loadingStatus),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
profileDetailInfo(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget profileDetailInfo() {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
_memberController.memberInfo.value.name!,
|
_memberController.memberInfo.value.name ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
@@ -325,38 +374,33 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
semanticLabel: '男',
|
semanticLabel: '男',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
|
if (_memberController.memberInfo.value.level != null)
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
|
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
|
||||||
height: 11,
|
height: 11,
|
||||||
semanticLabel:
|
semanticLabel: '等级${_memberController.memberInfo.value.level}',
|
||||||
'等级${_memberController.memberInfo.value.level}',
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
|
if (_memberController.memberInfo.value.vip?.status == 1) ...[
|
||||||
if (_memberController
|
if (_memberController
|
||||||
.memberInfo.value.vip!.status ==
|
.memberInfo.value.vip?.label?['img_label_uri_hans'] !=
|
||||||
1 &&
|
'')
|
||||||
_memberController.memberInfo.value.vip!
|
|
||||||
.label!['img_label_uri_hans'] !=
|
|
||||||
'') ...[
|
|
||||||
Image.network(
|
Image.network(
|
||||||
_memberController.memberInfo.value.vip!
|
_memberController
|
||||||
.label!['img_label_uri_hans'],
|
.memberInfo.value.vip!.label!['img_label_uri_hans'],
|
||||||
height: 20,
|
height: 20,
|
||||||
semanticLabel: _memberController
|
semanticLabel:
|
||||||
.memberInfo.value.vip!.label!['text'],
|
_memberController.memberInfo.value.vip!.label!['text'],
|
||||||
),
|
),
|
||||||
] else if (_memberController
|
if (_memberController.memberInfo.value.vip
|
||||||
.memberInfo.value.vip!.status ==
|
?.label?['img_label_uri_hans_static'] !=
|
||||||
1 &&
|
'')
|
||||||
_memberController.memberInfo.value.vip!
|
|
||||||
.label!['img_label_uri_hans_static'] !=
|
|
||||||
'') ...[
|
|
||||||
Image.network(
|
Image.network(
|
||||||
_memberController.memberInfo.value.vip!
|
_memberController.memberInfo.value.vip!
|
||||||
.label!['img_label_uri_hans_static'],
|
.label!['img_label_uri_hans_static'],
|
||||||
height: 20,
|
height: 20,
|
||||||
semanticLabel: _memberController
|
semanticLabel:
|
||||||
.memberInfo.value.vip!.label!['text'],
|
_memberController.memberInfo.value.vip!.label!['text'],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -371,24 +415,19 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
)),
|
)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
ClipboardData(
|
ClipboardData(text: _memberController.mid.toString()),
|
||||||
text: _memberController.mid.toString()),
|
|
||||||
);
|
);
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast('已复制${_memberController.mid}至剪贴板');
|
||||||
'已复制${_memberController.mid}至剪贴板');
|
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (_memberController
|
if (_memberController.memberInfo.value.official != null &&
|
||||||
.memberInfo.value.official!['title'] !=
|
_memberController.memberInfo.value.official!['title'] != '') ...[
|
||||||
'') ...[
|
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _memberController
|
text: _memberController.memberInfo.value.official!['role'] == 1
|
||||||
.memberInfo.value.official!['role'] ==
|
|
||||||
1
|
|
||||||
? '个人认证:'
|
? '个人认证:'
|
||||||
: '企业认证:',
|
: '企业认证:',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -396,8 +435,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _memberController
|
text: _memberController.memberInfo.value.official!['title'],
|
||||||
.memberInfo.value.official!['title'],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -405,24 +443,10 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
if (_memberController.memberInfo.value.sign != '')
|
|
||||||
SelectableText(
|
SelectableText(
|
||||||
_memberController.memberInfo.value.sign!,
|
_memberController.memberInfo.value.sign ?? '',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 骨架屏
|
|
||||||
return ProfilePanel(ctr: _memberController, loadingStatus: true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,7 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
MemberInfoModel memberInfo = ctr.memberInfo.value;
|
MemberInfoModel memberInfo = ctr.memberInfo.value;
|
||||||
return Builder(
|
return Builder(
|
||||||
builder: ((context) {
|
builder: ((context) {
|
||||||
return Padding(
|
return Row(
|
||||||
padding:
|
|
||||||
EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: ctr.heroTag!,
|
tag: ctr.heroTag!,
|
||||||
@@ -181,14 +178,12 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
Obx(
|
Obx(
|
||||||
() => Expanded(
|
() => Expanded(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () => ctr.actionRelationMod(),
|
onPressed: () => ctr.actionRelationMod(context),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: ctr.attribute.value == -1
|
foregroundColor: ctr.attribute.value == -1
|
||||||
? Colors.transparent
|
? Colors.transparent
|
||||||
: ctr.attribute.value != 0
|
: ctr.attribute.value != 0
|
||||||
? Theme.of(context)
|
? Theme.of(context).colorScheme.outline
|
||||||
.colorScheme
|
|
||||||
.outline
|
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.onPrimary,
|
.onPrimary,
|
||||||
@@ -232,8 +227,7 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(left: 80, right: 80),
|
padding: const EdgeInsets.only(left: 80, right: 80),
|
||||||
foregroundColor:
|
foregroundColor:
|
||||||
Theme.of(context).colorScheme.onPrimary,
|
Theme.of(context).colorScheme.onPrimary,
|
||||||
backgroundColor:
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
),
|
||||||
child: const Text('个人中心(web)'),
|
child: const Text('个人中心(web)'),
|
||||||
)
|
)
|
||||||
@@ -243,8 +237,7 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: const EdgeInsets.only(left: 80, right: 80),
|
padding: const EdgeInsets.only(left: 80, right: 80),
|
||||||
foregroundColor:
|
foregroundColor: Theme.of(context).colorScheme.outline,
|
||||||
Theme.of(context).colorScheme.outline,
|
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).colorScheme.onInverseSurface,
|
Theme.of(context).colorScheme.onInverseSurface,
|
||||||
),
|
),
|
||||||
@@ -255,7 +248,6 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:PiliPalaX/common/widgets/badge.dart';
|
|||||||
import 'package:PiliPalaX/models/member/seasons.dart';
|
import 'package:PiliPalaX/models/member/seasons.dart';
|
||||||
import 'package:PiliPalaX/pages/member_seasons/widgets/item.dart';
|
import 'package:PiliPalaX/pages/member_seasons/widgets/item.dart';
|
||||||
|
|
||||||
|
import '../../../utils/grid.dart';
|
||||||
|
|
||||||
class MemberSeasonsPanel extends StatelessWidget {
|
class MemberSeasonsPanel extends StatelessWidget {
|
||||||
final MemberSeasonsDataModel? data;
|
final MemberSeasonsDataModel? data;
|
||||||
const MemberSeasonsPanel({super.key, this.data});
|
const MemberSeasonsPanel({super.key, this.data});
|
||||||
@@ -38,10 +40,9 @@ class MemberSeasonsPanel extends StatelessWidget {
|
|||||||
size: 'small',
|
size: 'small',
|
||||||
text: item.meta!.total.toString(),
|
text: item.meta!.total.toString(),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 35,
|
width: 30,
|
||||||
height: 35,
|
height: 30,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: '前往',
|
tooltip: '前往',
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
@@ -50,7 +51,7 @@ class MemberSeasonsPanel extends StatelessWidget {
|
|||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.arrow_forward,
|
Icons.arrow_forward_ios,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -61,12 +62,12 @@ class MemberSeasonsPanel extends StatelessWidget {
|
|||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
gridDelegate:
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisCount: 2, // Use a fixed count for GridView
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
maxCrossAxisExtent: Grid.maxRowWidth,
|
||||||
mainAxisSpacing: StyleString.safeSpace,
|
|
||||||
childAspectRatio: 0.94,
|
childAspectRatio: 0.94,
|
||||||
|
mainAxisExtent: 0,
|
||||||
),
|
),
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
controller: _memberArchivesController.scrollController,
|
controller: _memberArchivesController.scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
FutureBuilder(
|
SliverPadding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
||||||
|
sliver: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
@@ -77,16 +80,14 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
|
|||||||
() => list.isNotEmpty
|
() => list.isNotEmpty
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate:
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent:
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
|
||||||
context,
|
|
||||||
Grid.maxRowWidth * 2,
|
Grid.maxRowWidth * 2,
|
||||||
StyleString.safeSpace) /
|
childAspectRatio:
|
||||||
2.1 /
|
StyleString.aspectRatio * 2.3,
|
||||||
StyleString.aspectRatio),
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, index) {
|
(BuildContext context, index) {
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
@@ -117,6 +118,7 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:PiliPalaX/utils/utils.dart';
|
|||||||
import '../../common/constants.dart';
|
import '../../common/constants.dart';
|
||||||
import '../../common/widgets/http_error.dart';
|
import '../../common/widgets/http_error.dart';
|
||||||
import '../../utils/grid.dart';
|
import '../../utils/grid.dart';
|
||||||
|
import '../../utils/storage.dart';
|
||||||
import '../dynamics/widgets/dynamic_panel.dart';
|
import '../dynamics/widgets/dynamic_panel.dart';
|
||||||
import 'package:waterfall_flow/waterfall_flow.dart';
|
import 'package:waterfall_flow/waterfall_flow.dart';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
|
|||||||
late Future _futureBuilderFuture;
|
late Future _futureBuilderFuture;
|
||||||
late ScrollController scrollController;
|
late ScrollController scrollController;
|
||||||
late int mid;
|
late int mid;
|
||||||
|
late bool dynamicsWaterfallFlow;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -44,6 +46,8 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
dynamicsWaterfallFlow = GStrorage.setting
|
||||||
|
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -72,9 +76,29 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
|
|||||||
Map data = snapshot.data as Map;
|
Map data = snapshot.data as Map;
|
||||||
List list = _memberDynamicController.dynamicsList;
|
List list = _memberDynamicController.dynamicsList;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return Obx(() {
|
||||||
() => list.isNotEmpty
|
if (list.isEmpty) {
|
||||||
? SliverWaterfallFlow.extent(
|
return const SliverToBoxAdapter();
|
||||||
|
}
|
||||||
|
if (!dynamicsWaterfallFlow) {
|
||||||
|
return SliverCrossAxisGroup(
|
||||||
|
slivers: [
|
||||||
|
const SliverFillRemaining(),
|
||||||
|
SliverConstrainedCrossAxis(
|
||||||
|
maxExtent: Grid.maxRowWidth * 2,
|
||||||
|
sliver: SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
return DynamicPanel(item: list[index]);
|
||||||
|
},
|
||||||
|
childCount: list.length,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SliverFillRemaining(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SliverWaterfallFlow.extent(
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
//cacheExtent: 0.0,
|
//cacheExtent: 0.0,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
@@ -95,9 +119,8 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
|
|||||||
: LastChildLayoutType.none,
|
: LastChildLayoutType.none,
|
||||||
children: [
|
children: [
|
||||||
for (var i in list) DynamicPanel(item: i),
|
for (var i in list) DynamicPanel(item: i),
|
||||||
])
|
]);
|
||||||
: const SliverToBoxAdapter(),
|
});
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: snapshot.data['msg'],
|
errMsg: snapshot.data['msg'],
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import 'package:PiliPalaX/common/widgets/http_error.dart';
|
|||||||
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/video_card_h.dart';
|
import 'package:PiliPalaX/common/widgets/video_card_h.dart';
|
||||||
|
|
||||||
|
import '../../common/constants.dart';
|
||||||
|
import '../../utils/grid.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
class MemberSearchPage extends StatefulWidget {
|
class MemberSearchPage extends StatefulWidget {
|
||||||
@@ -82,116 +84,98 @@ class _MemberSearchPageState extends State<MemberSearchPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Obx(
|
body: Obx(
|
||||||
() => Column(
|
() {
|
||||||
children: _memberSearchCtr.loadingStatus.value == 'init'
|
if (_memberSearchCtr.loadingStatus.value == 'init') {
|
||||||
? [
|
return Center(
|
||||||
Expanded(
|
|
||||||
child: Center(
|
|
||||||
child: Text('搜索「${_memberSearchCtr.uname.value}」的动态、视频'),
|
child: Text('搜索「${_memberSearchCtr.uname.value}」的动态、视频'),
|
||||||
),
|
);
|
||||||
),
|
}
|
||||||
]
|
return CustomScrollView(
|
||||||
: [
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
// TabBar(
|
controller: scrollController,
|
||||||
// controller: _tabController,
|
slivers: <Widget>[
|
||||||
// tabs: const [
|
|
||||||
// Tab(text: "视频"),
|
|
||||||
// Tab(text: "动态"),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
Expanded(
|
|
||||||
child:
|
|
||||||
// TabBarView(
|
|
||||||
// controller: _tabController,
|
|
||||||
// children: [
|
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _memberSearchCtr.searchArchives(),
|
future: _memberSearchCtr.searchArchives(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.hasData) {
|
||||||
Map data = snapshot.data as Map;
|
Map data = snapshot.data as Map;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return SliverPadding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.all(StyleString.safeSpace),
|
||||||
|
sliver: Obx(
|
||||||
() => _memberSearchCtr.archiveList.isNotEmpty
|
() => _memberSearchCtr.archiveList.isNotEmpty
|
||||||
? ListView.builder(
|
? SliverGrid(
|
||||||
controller: scrollController,
|
gridDelegate:
|
||||||
itemCount:
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
_memberSearchCtr.archiveList.length +
|
mainAxisSpacing: StyleString
|
||||||
1,
|
.safeSpace,
|
||||||
itemBuilder: (context, index) {
|
crossAxisSpacing: StyleString
|
||||||
if (index ==
|
.safeSpace,
|
||||||
_memberSearchCtr
|
maxCrossAxisExtent: Grid.maxRowWidth *
|
||||||
.archiveList.length) {
|
2,
|
||||||
return Container(
|
childAspectRatio:
|
||||||
height: MediaQuery.of(context)
|
StyleString.aspectRatio * 2.3,
|
||||||
.padding
|
mainAxisExtent: 0),
|
||||||
.bottom +
|
delegate: SliverChildBuilderDelegate(
|
||||||
60,
|
(context, index) {
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context)
|
|
||||||
.padding
|
|
||||||
.bottom),
|
|
||||||
child: Center(
|
|
||||||
child: Obx(
|
|
||||||
() => Text(
|
|
||||||
_memberSearchCtr
|
|
||||||
.loadingText.value,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline,
|
|
||||||
fontSize: 13),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: _memberSearchCtr
|
videoItem: _memberSearchCtr
|
||||||
.archiveList[index]);
|
.archiveList[index]);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
childCount: _memberSearchCtr.archiveList
|
||||||
|
.length))
|
||||||
: _memberSearchCtr.loadingStatus.value ==
|
: _memberSearchCtr.loadingStatus.value ==
|
||||||
'loading'
|
'loading'
|
||||||
? ListView.builder(
|
? SliverGrid(
|
||||||
itemCount: 10,
|
gridDelegate:
|
||||||
itemBuilder: (context, index) {
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing:
|
||||||
|
StyleString.cardSpace,
|
||||||
|
crossAxisSpacing:
|
||||||
|
StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent:
|
||||||
|
Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio:
|
||||||
|
StyleString.aspectRatio *
|
||||||
|
2.1,
|
||||||
|
mainAxisExtent: 0),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
},
|
}, childCount: 10))
|
||||||
)
|
: const NoData(),
|
||||||
: const CustomScrollView(
|
));
|
||||||
slivers: <Widget>[
|
|
||||||
NoData(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return CustomScrollView(
|
return HttpError(
|
||||||
slivers: <Widget>[
|
|
||||||
HttpError(
|
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => setState(() {}),
|
fn: () => setState(() {}),
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return ListView.builder(
|
return SliverPadding(
|
||||||
itemCount: 10,
|
padding: const EdgeInsets.all(StyleString.safeSpace),
|
||||||
itemBuilder: (context, index) {
|
sliver: SliverGrid(
|
||||||
|
gridDelegate:
|
||||||
|
SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio:
|
||||||
|
StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
|
delegate:
|
||||||
|
SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
},
|
}, childCount: 10)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,10 @@ class _MinePageState extends State<MinePage> {
|
|||||||
semanticsLabel: '头像',
|
semanticsLabel: '头像',
|
||||||
width: 85,
|
width: 85,
|
||||||
height: 85)
|
height: 85)
|
||||||
: Image.asset('assets/images/noface.jpeg',semanticLabel: "默认头像",),
|
: Image.asset(
|
||||||
|
'assets/images/noface.jpeg',
|
||||||
|
semanticLabel: "默认头像",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -183,8 +186,8 @@ class _MinePageState extends State<MinePage> {
|
|||||||
style:
|
style:
|
||||||
TextStyle(color: Theme.of(context).colorScheme.outline)),
|
TextStyle(color: Theme.of(context).colorScheme.outline)),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: (_mineController.userInfo.value.money ?? '-')
|
text:
|
||||||
.toString(),
|
(_mineController.userInfo.value.money ?? '-').toString(),
|
||||||
style:
|
style:
|
||||||
TextStyle(color: Theme.of(context).colorScheme.primary)),
|
TextStyle(color: Theme.of(context).colorScheme.primary)),
|
||||||
]))
|
]))
|
||||||
@@ -256,12 +259,12 @@ class _MinePageState extends State<MinePage> {
|
|||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
fontWeight: FontWeight.bold);
|
fontWeight: FontWeight.bold);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: constraints.maxWidth / 3 * 0.6,
|
height: constraints.maxWidth * 0.33 * 0.6,
|
||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
primary: false,
|
primary: false,
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
childAspectRatio: 1.67,
|
childAspectRatio: 1.66,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => _mineController.pushDynamic(),
|
onTap: () => _mineController.pushDynamic(),
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.back();
|
Get.back();
|
||||||
DownloadUtils.downloadImg(_previewController.currentImgUrl);
|
DownloadUtils.downloadImg(
|
||||||
|
context, _previewController.currentImgUrl);
|
||||||
},
|
},
|
||||||
dense: true,
|
dense: true,
|
||||||
title: const Text('保存到手机', style: TextStyle(fontSize: 14)),
|
title: const Text('保存到手机', style: TextStyle(fontSize: 14)),
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class RankController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
void animateToTop() {
|
void animateToTop() {
|
||||||
int index = tabController.index;
|
int index = tabController.index;
|
||||||
var ctr = tabsCtrList[index];
|
var ctr = tabsCtrList[index];
|
||||||
ctr().animateToTop();
|
ctr.animateToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTabConfig() async {
|
void setTabConfig() async {
|
||||||
|
|||||||
@@ -35,41 +35,63 @@ class _RankPageState extends State<RankPage>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_rankController.tabController.removeListener(() {});
|
_rankController.tabController.removeListener(() {});
|
||||||
_rankController.tabController.dispose();
|
_rankController.tabController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Row(
|
||||||
appBar: AppBar(
|
|
||||||
toolbarHeight: 0,
|
|
||||||
elevation: 0,
|
|
||||||
systemOverlayStyle: SystemUiOverlayStyle(
|
|
||||||
// Customize the status bar here
|
|
||||||
statusBarIconBrightness:
|
|
||||||
MediaQuery.of(context).platformBrightness == Brightness.dark
|
|
||||||
? Brightness.light
|
|
||||||
: Brightness.dark,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: Row(
|
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: StyleString.cardSpace,
|
width: StyleString.cardSpace,
|
||||||
),
|
),
|
||||||
|
// SizedBox(
|
||||||
|
// width: 55,
|
||||||
|
// child: NavigationRail(
|
||||||
|
//
|
||||||
|
// backgroundColor: Colors.transparent,
|
||||||
|
// minWidth: 50.0,
|
||||||
|
// // elevation: 0,
|
||||||
|
// selectedIndex: _selectedTabIndex,
|
||||||
|
// onDestinationSelected: (int index) {
|
||||||
|
// feedBack();
|
||||||
|
// if (_selectedTabIndex == index) {
|
||||||
|
// _rankController.tabsCtrList[index]().animateToTop();
|
||||||
|
// } else {
|
||||||
|
// setState(() {
|
||||||
|
// _rankController.tabController.index = index;
|
||||||
|
// _selectedTabIndex = index;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// labelType: NavigationRailLabelType.none,
|
||||||
|
// destinations: [
|
||||||
|
// for (var tab in _rankController.tabs)
|
||||||
|
// NavigationRailDestination(
|
||||||
|
// padding: EdgeInsets.zero,
|
||||||
|
// icon: Text(tab['label']),
|
||||||
|
// // selectedIcon: Text(tab['label']),
|
||||||
|
// label: const SizedBox.shrink(),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// trailing: const SizedBox(height: 100),
|
||||||
|
// )),
|
||||||
LayoutBuilder(builder: (context, constraint) {
|
LayoutBuilder(builder: (context, constraint) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints:
|
constraints: BoxConstraints(minHeight: constraint.maxHeight),
|
||||||
BoxConstraints(minHeight: constraint.maxHeight + 100),
|
|
||||||
child: IntrinsicHeight(
|
child: IntrinsicHeight(
|
||||||
child: NavigationRail(
|
child: NavigationRail(
|
||||||
minWidth: 55.0,
|
backgroundColor: Colors.transparent,
|
||||||
|
minWidth: 50.0,
|
||||||
|
// elevation: 0,
|
||||||
selectedIndex: _selectedTabIndex,
|
selectedIndex: _selectedTabIndex,
|
||||||
onDestinationSelected: (int index) {
|
onDestinationSelected: (int index) {
|
||||||
feedBack();
|
feedBack();
|
||||||
@@ -91,6 +113,7 @@ class _RankPageState extends State<RankPage>
|
|||||||
label: const SizedBox.shrink(),
|
label: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
trailing: const SizedBox(height: 100),
|
||||||
))));
|
))));
|
||||||
}),
|
}),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -100,7 +123,6 @@ class _RankPageState extends State<RankPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ class ZonePage extends StatefulWidget {
|
|||||||
class _ZonePageState extends State<ZonePage>
|
class _ZonePageState extends State<ZonePage>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
late ZoneController _zoneController;
|
late ZoneController _zoneController;
|
||||||
List videoList = [];
|
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
late ScrollController scrollController;
|
late ScrollController scrollController;
|
||||||
|
|
||||||
@@ -81,12 +80,12 @@ class _ZonePageState extends State<ZonePage>
|
|||||||
return await _zoneController.onRefresh();
|
return await _zoneController.onRefresh();
|
||||||
},
|
},
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
controller: _zoneController.scrollController,
|
controller: scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
// 单列布局 EdgeInsets.zero
|
// 单列布局 EdgeInsets.zero
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.fromLTRB(0, StyleString.safeSpace - 5, 0, 0),
|
const EdgeInsets.fromLTRB(StyleString.safeSpace, StyleString.safeSpace, 0, 0),
|
||||||
sliver: FutureBuilder(
|
sliver: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@@ -95,19 +94,12 @@ class _ZonePageState extends State<ZonePage>
|
|||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverGrid(
|
() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
// 行间距
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
// 列间距
|
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
|
||||||
// 最大宽度
|
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(context,
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
Grid.maxRowWidth * 2, StyleString.cardSpace,
|
mainAxisExtent: 0),
|
||||||
screenWidthOffset:
|
|
||||||
StyleString.cardSpace + 55) /
|
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: _zoneController.videoList[index],
|
videoItem: _zoneController.videoList[index],
|
||||||
@@ -138,7 +130,13 @@ class _ZonePageState extends State<ZonePage>
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 10),
|
}, childCount: 10),
|
||||||
|
|||||||
@@ -146,17 +146,15 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
|
|
||||||
Widget contentGrid(ctr, videoList) {
|
Widget contentGrid(ctr, videoList) {
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
// 行间距
|
// 行间距
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
// 列间距
|
// 列间距
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
// 最大宽度
|
// 最大宽度
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth,
|
maxCrossAxisExtent: Grid.maxRowWidth,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio: StyleString.aspectRatio,
|
||||||
context, Grid.maxRowWidth, StyleString.safeSpace) /
|
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
|
||||||
StyleString.aspectRatio +
|
|
||||||
MediaQuery.textScalerOf(context).scale(90),
|
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import 'package:PiliPalaX/common/skeleton/video_card_h.dart';
|
|||||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||||
import 'package:PiliPalaX/models/common/search_type.dart';
|
import 'package:PiliPalaX/models/common/search_type.dart';
|
||||||
|
|
||||||
|
import '../../common/constants.dart';
|
||||||
|
import '../../utils/grid.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/article_panel.dart';
|
import 'widgets/article_panel.dart';
|
||||||
import 'widgets/live_panel.dart';
|
import 'widgets/live_panel.dart';
|
||||||
@@ -132,11 +134,18 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return ListView.builder(
|
return CustomScrollView(
|
||||||
addAutomaticKeepAlives: false,
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
addRepaintBoundaries: false,
|
slivers: [
|
||||||
itemCount: 15,
|
SliverGrid(
|
||||||
itemBuilder: (context, index) {
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
switch (widget.searchType) {
|
switch (widget.searchType) {
|
||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -150,7 +159,9 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
childCount: 15,
|
||||||
|
))
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ Widget searchArticlePanel(BuildContext context, ctr, list) {
|
|||||||
color: Theme.of(context).colorScheme.outline);
|
color: Theme.of(context).colorScheme.outline);
|
||||||
return CustomScrollView(controller: ctr.scrollController, slivers: [
|
return CustomScrollView(controller: ctr.scrollController, slivers: [
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
context, Grid.maxRowWidth * 2, StyleString.safeSpace) /
|
mainAxisExtent: 0),
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
@@ -32,8 +30,8 @@ Widget searchArticlePanel(BuildContext context, ctr, list) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.symmetric(
|
||||||
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
horizontal: StyleString.safeSpace),
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
final double width = (boxConstraints.maxWidth -
|
final double width = (boxConstraints.maxWidth -
|
||||||
StyleString.cardSpace *
|
StyleString.cardSpace *
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
|||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
primary: false,
|
primary: false,
|
||||||
controller: ctr!.scrollController,
|
controller: ctr!.scrollController,
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth,
|
maxCrossAxisExtent: Grid.maxRowWidth,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
mainAxisSpacing: StyleString.safeSpace,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio: StyleString.aspectRatio,
|
||||||
context, Grid.maxRowWidth, StyleString.safeSpace) /
|
mainAxisExtent: MediaQuery.textScalerOf(context).scale(80),
|
||||||
StyleString.aspectRatio +
|
|
||||||
MediaQuery.textScalerOf(context).scale(80),
|
|
||||||
),
|
),
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
slivers: [
|
slivers: [
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: 157,
|
mainAxisExtent: 160,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
|
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
|
||||||
var i = list![index];
|
var i = list![index];
|
||||||
@@ -36,8 +36,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
// });
|
// });
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(StyleString.safeSpace,
|
||||||
StyleString.safeSpace, 7, StyleString.safeSpace, 2),
|
StyleString.safeSpace, StyleString.safeSpace, 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -125,7 +125,10 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
EpisodeItem episode =
|
EpisodeItem episode =
|
||||||
res['data'].episodes.first;
|
res['data'].episodes.first;
|
||||||
int? epId = res['data'].userStatus?.progress?.lastEpId;
|
int? epId = res['data']
|
||||||
|
.userStatus
|
||||||
|
?.progress
|
||||||
|
?.lastEpId;
|
||||||
if (epId == null) {
|
if (epId == null) {
|
||||||
epId = episode.epId;
|
epId = episode.epId;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
width: context.width,
|
width: context.width,
|
||||||
height: 34,
|
height: 34,
|
||||||
padding: const EdgeInsets.only(left: 8, top: 0, right: 12),
|
padding: const EdgeInsets.only(
|
||||||
|
left: StyleString.safeSpace, top: 0, right: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -69,7 +70,7 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
onPressed: () => controller.onShowFilterDialog(ctr),
|
onPressed: () => controller.onShowFilterDialog(context, ctr),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.filter_list_outlined,
|
Icons.filter_list_outlined,
|
||||||
size: 18,
|
size: 18,
|
||||||
@@ -84,22 +85,23 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
controller: ctr.scrollController,
|
controller: ctr.scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverGrid(
|
SliverPadding(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
padding: const EdgeInsets.all(StyleString.safeSpace),
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
sliver: SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(context,
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
Grid.maxRowWidth * 2, StyleString.safeSpace) /
|
mainAxisExtent: 0),
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return VideoCardH(videoItem: list[index], showPubdate: true);
|
return VideoCardH(
|
||||||
|
videoItem: list[index], showPubdate: true);
|
||||||
},
|
},
|
||||||
childCount: list.length,
|
childCount: list.length,
|
||||||
),
|
),
|
||||||
),
|
)),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
@@ -176,10 +178,10 @@ class VideoPanelController extends GetxController {
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
onShowFilterDialog(searchPanelCtr) {
|
onShowFilterDialog(BuildContext context, SearchPanelController searchPanelCtr) {
|
||||||
SmartDialog.show(
|
showDialog(
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
TextStyle textStyle = Theme.of(context).textTheme.titleMedium!;
|
TextStyle textStyle = Theme.of(context).textTheme.titleMedium!;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('时长筛选'),
|
title: const Text('时长筛选'),
|
||||||
|
|||||||
@@ -48,17 +48,16 @@ class SettingController extends GetxController {
|
|||||||
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0);
|
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
loginOut() async {
|
loginOut(BuildContext context) async {
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: const Text('确认要退出登录吗'),
|
content: const Text('确认要退出登录吗'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: const Text('点错了'),
|
child: const Text('点错了'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -73,7 +72,7 @@ class SettingController extends GetxController {
|
|||||||
.put(LocalCacheKey.accessKey, {'mid': -1, 'value': ''});
|
.put(LocalCacheKey.accessKey, {'mid': -1, 'value': ''});
|
||||||
|
|
||||||
await LoginUtils.refreshLoginStatus(false);
|
await LoginUtils.refreshLoginStatus(false);
|
||||||
SmartDialog.dismiss().then((value) => Get.back());
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,10 +54,9 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
var systemProxyHost = '';
|
var systemProxyHost = '';
|
||||||
var systemProxyPort = '';
|
var systemProxyPort = '';
|
||||||
|
|
||||||
SmartDialog.show(
|
showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('设置代理'),
|
title: const Text('设置代理'),
|
||||||
content: Column(
|
content: Column(
|
||||||
@@ -101,7 +100,7 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
@@ -112,7 +111,7 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
setting.put(SettingBoxKey.systemProxyHost, systemProxyHost);
|
setting.put(SettingBoxKey.systemProxyHost, systemProxyHost);
|
||||||
setting.put(SettingBoxKey.systemProxyPort, systemProxyPort);
|
setting.put(SettingBoxKey.systemProxyPort, systemProxyPort);
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
// Request.dio;
|
// Request.dio;
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
@@ -210,6 +209,13 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
setKey: SettingBoxKey.disableLikeMsg,
|
setKey: SettingBoxKey.disableLikeMsg,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '默认展示评论区',
|
||||||
|
subTitle: '在视频详情页默认切换至评论区页(仅tab型布局)',
|
||||||
|
leading: Icon(Icons.mode_comment_outlined),
|
||||||
|
setKey: SettingBoxKey.defaultShowComment,
|
||||||
|
defaultVal: false,
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('评论展示', style: titleStyle),
|
title: Text('评论展示', style: titleStyle),
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ class FontSizeSelectPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _FontSizeSelectPageState extends State<FontSizeSelectPage> {
|
class _FontSizeSelectPageState extends State<FontSizeSelectPage> {
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
List<double> list = [0.9, 0.95, 1.0, 1.05, 1.1, 1.15, 1.2, 1.25, 1.3];
|
List<double> list = List.generate(12, (index) => 0.85 + index * 0.05);
|
||||||
|
//[0.85, 0.9, 0.95, 1.0, 1.05, 1.1, 1.15, 1.2, 1.25, 1.3, 1.35];
|
||||||
late double minsize;
|
late double minsize;
|
||||||
late double maxSize;
|
late double maxSize;
|
||||||
late double currentSize;
|
late double currentSize;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class _TabbarSetPageState extends State<TabbarSetPage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
defaultTabs = tabsConfig;
|
defaultTabs = tabsConfig;
|
||||||
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
||||||
defaultValue: ['live', 'rcmd', 'hot', 'bangumi']);
|
defaultValue: ['live', 'rcmd', 'hot', 'rank', 'bangumi']);
|
||||||
// 对 tabData 进行排序
|
// 对 tabData 进行排序
|
||||||
defaultTabs.sort((a, b) {
|
defaultTabs.sort((a, b) {
|
||||||
int indexA = tabbarSort.indexOf((a['type'] as TabType).id);
|
int indexA = tabbarSort.indexOf((a['type'] as TabType).id);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:PiliPalaX/pages/setting/widgets/switch_item.dart';
|
import 'package:PiliPalaX/pages/setting/widgets/switch_item.dart';
|
||||||
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
||||||
@@ -76,10 +77,9 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
|
|||||||
// 添加自定义倍速
|
// 添加自定义倍速
|
||||||
void onAddSpeed() {
|
void onAddSpeed() {
|
||||||
double customSpeed = 1.0;
|
double customSpeed = 1.0;
|
||||||
SmartDialog.show(
|
showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('添加倍速'),
|
title: const Text('添加倍速'),
|
||||||
content: Column(
|
content: Column(
|
||||||
@@ -103,7 +103,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: const Text('取消'),
|
child: const Text('取消'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -112,7 +112,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
|
|||||||
await videoStorage.put(
|
await videoStorage.put(
|
||||||
VideoBoxKey.customSpeedsList, customSpeedsList);
|
VideoBoxKey.customSpeedsList, customSpeedsList);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('确认添加'),
|
child: const Text('确认添加'),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '竖屏扩大展示',
|
title: '竖屏扩大展示',
|
||||||
subTitle: '小屏竖屏视频宽高比由16:9扩大至4:5(!暂不支持临时收起)',
|
subTitle: '小屏竖屏视频宽高比由16:9扩大至1:1(不支持收起);横屏适配时,扩大至9:16',
|
||||||
leading: Icon(Icons.expand_outlined),
|
leading: Icon(Icons.expand_outlined),
|
||||||
setKey: SettingBoxKey.enableVerticalExpand,
|
setKey: SettingBoxKey.enableVerticalExpand,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
@@ -151,7 +151,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
subTitle: '进入后台时继续播放',
|
subTitle: '进入后台时继续播放',
|
||||||
leading: Icon(Icons.motion_photos_pause_outlined),
|
leading: Icon(Icons.motion_photos_pause_outlined),
|
||||||
setKey: SettingBoxKey.continuePlayInBackground,
|
setKey: SettingBoxKey.continuePlayInBackground,
|
||||||
defaultVal: true,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
SetSwitchItem(
|
SetSwitchItem(
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
)),
|
)),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SmartDialog.show(
|
showDialog(
|
||||||
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('查看详情'),
|
title: const Text('查看详情'),
|
||||||
@@ -130,7 +131,7 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
)
|
)
|
||||||
@@ -150,9 +151,9 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void import_export_cookies(TextStyle titleStyle, TextStyle subTitleStyle) {
|
void import_export_cookies(TextStyle titleStyle, TextStyle subTitleStyle) {
|
||||||
SmartDialog.show(
|
showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return SimpleDialog(
|
return SimpleDialog(
|
||||||
title: const Text('导入/导出cookie', style: TextStyle(color: Colors.red)),
|
title: const Text('导入/导出cookie', style: TextStyle(color: Colors.red)),
|
||||||
children: [
|
children: [
|
||||||
@@ -175,13 +176,14 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
),
|
),
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await SmartDialog.dismiss();
|
Navigator.of(context).pop();
|
||||||
if (!userLogin) {
|
if (!userLogin) {
|
||||||
SmartDialog.showToast('请先登录');
|
SmartDialog.showToast('请先登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String cookie = await CookieTool.exportCookie();
|
final String cookie = await CookieTool.exportCookie();
|
||||||
await SmartDialog.show(
|
await showDialog(
|
||||||
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('导出cookie(危险)',
|
title: const Text('导出cookie(危险)',
|
||||||
@@ -190,7 +192,7 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await SmartDialog.dismiss();
|
Navigator.of(context).pop();
|
||||||
await Clipboard.setData(
|
await Clipboard.setData(
|
||||||
ClipboardData(text: cookie));
|
ClipboardData(text: cookie));
|
||||||
},
|
},
|
||||||
@@ -199,7 +201,7 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await SmartDialog.dismiss();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text('取消'),
|
child: const Text('取消'),
|
||||||
),
|
),
|
||||||
@@ -223,13 +225,14 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
),
|
),
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await SmartDialog.dismiss();
|
|
||||||
ClipboardData? data = await Clipboard.getData('text/plain');
|
ClipboardData? data = await Clipboard.getData('text/plain');
|
||||||
if (data == null || data.text == null || data.text == '') {
|
if (data == null || data.text == null || data.text == '') {
|
||||||
SmartDialog.showToast('未检测到剪贴板内容');
|
SmartDialog.showToast('未检测到剪贴板内容');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await SmartDialog.show(
|
if (!context.mounted) return;
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('导入剪贴板中的cookie'),
|
title: const Text('导入剪贴板中的cookie'),
|
||||||
@@ -237,13 +240,13 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('取消'),
|
child: const Text('取消'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await SmartDialog.dismiss();
|
Get.back();
|
||||||
final String cookie = data.text!;
|
final String cookie = data.text!;
|
||||||
try {
|
try {
|
||||||
await CookieTool.importCookie(cookie);
|
await CookieTool.importCookie(cookie);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:PiliPalaX/models/common/rcmd_type.dart';
|
|||||||
import 'package:PiliPalaX/pages/setting/widgets/select_dialog.dart';
|
import 'package:PiliPalaX/pages/setting/widgets/select_dialog.dart';
|
||||||
import 'package:PiliPalaX/utils/recommend_filter.dart';
|
import 'package:PiliPalaX/utils/recommend_filter.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import 'widgets/switch_item.dart';
|
import 'widgets/switch_item.dart';
|
||||||
|
|
||||||
@@ -93,8 +94,9 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 显示一个确认框,告知用户可能会导致账号被风控
|
// 显示一个确认框,告知用户可能会导致账号被风控
|
||||||
SmartDialog.show(
|
if (!context.mounted) return;
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
await showDialog(
|
||||||
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
@@ -104,14 +106,20 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
result = null;
|
result = null;
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('取消'),
|
child: const Text('取消'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
await MemberHttp.cookieToKey();
|
var res = await MemberHttp.cookieToKey();
|
||||||
|
if (res['status']) {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(
|
||||||
|
'获取access_key失败:${res['msg']}');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: const Text('确定'),
|
child: const Text('确定'),
|
||||||
),
|
),
|
||||||
@@ -254,10 +262,9 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
'* 其它(如热门视频、手动搜索、链接跳转等)均不受过滤器影响。\n'
|
'* 其它(如热门视频、手动搜索、链接跳转等)均不受过滤器影响。\n'
|
||||||
'* 设定较严苛的条件可导致推荐项数锐减或多次请求,请酌情选择。\n'
|
'* 设定较严苛的条件可导致推荐项数锐减或多次请求,请酌情选择。\n'
|
||||||
'* 后续可能会增加更多过滤条件,敬请期待。',
|
'* 后续可能会增加更多过滤条件,敬请期待。',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.labelSmall!.copyWith(
|
||||||
.textTheme
|
color:
|
||||||
.labelSmall!
|
Theme.of(context).colorScheme.outline.withOpacity(0.7)),
|
||||||
.copyWith(color: Theme.of(context).colorScheme.outline.withOpacity(0.7)),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'package:PiliPalaX/utils/global_data.dart';
|
|||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
|
||||||
import '../../models/common/dynamic_badge_mode.dart';
|
import '../../models/common/dynamic_badge_mode.dart';
|
||||||
|
import '../../models/common/up_panel_position.dart';
|
||||||
import '../../plugin/pl_player/utils/fullscreen.dart';
|
import '../../plugin/pl_player/utils/fullscreen.dart';
|
||||||
import '../../models/common/nav_bar_config.dart';
|
import '../../models/common/nav_bar_config.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@@ -34,13 +35,18 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
late int picQuality;
|
late int picQuality;
|
||||||
late ThemeType _tempThemeValue;
|
late ThemeType _tempThemeValue;
|
||||||
late double maxRowWidth;
|
late double maxRowWidth;
|
||||||
|
late UpPanelPosition upPanelPosition;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
|
picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
|
||||||
_tempThemeValue = settingController.themeType.value;
|
_tempThemeValue = settingController.themeType.value;
|
||||||
maxRowWidth = setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double;
|
maxRowWidth =
|
||||||
|
setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double;
|
||||||
|
upPanelPosition = UpPanelPosition.values[setting.get(
|
||||||
|
SettingBoxKey.upPanelPosition,
|
||||||
|
defaultValue: UpPanelPosition.leftFixed.code)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -70,15 +76,15 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
callFn: (value) {
|
callFn: (value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
autoScreen();
|
autoScreen();
|
||||||
SmartDialog.showToast('已开启横屏适配');
|
SmartDialog.showToast('已开启横屏适配,推荐将全屏方式设为【不改变当前方向】');
|
||||||
} else {
|
} else {
|
||||||
AutoOrientation.portraitUpMode();
|
AutoOrientation.portraitUpMode();
|
||||||
SmartDialog.showToast('已关闭横屏适配');
|
SmartDialog.showToast('已关闭横屏适配');
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '自适应底栏/侧边栏',
|
title: '改用侧边栏',
|
||||||
subTitle: '横竖屏自动切换(其它底栏设置失效)',
|
subTitle: '开启后底栏被替换,且底栏相关设置失效',
|
||||||
leading: Icon(Icons.chrome_reader_mode_outlined),
|
leading: Icon(Icons.chrome_reader_mode_outlined),
|
||||||
setKey: SettingBoxKey.adaptiveNavBar,
|
setKey: SettingBoxKey.adaptiveNavBar,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
@@ -90,22 +96,6 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
setKey: SettingBoxKey.enableMYBar,
|
setKey: SettingBoxKey.enableMYBar,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
|
||||||
title: '首页顶栏收起',
|
|
||||||
subTitle: '首页列表滑动时,收起顶栏',
|
|
||||||
leading: Icon(Icons.vertical_align_top_outlined),
|
|
||||||
setKey: SettingBoxKey.hideSearchBar,
|
|
||||||
defaultVal: false,
|
|
||||||
needReboot: true,
|
|
||||||
),
|
|
||||||
const SetSwitchItem(
|
|
||||||
title: '首页底栏收起',
|
|
||||||
subTitle: '首页列表滑动时,收起底栏',
|
|
||||||
leading: Icon(Icons.vertical_align_bottom_outlined),
|
|
||||||
setKey: SettingBoxKey.hideTabBar,
|
|
||||||
defaultVal: false,
|
|
||||||
needReboot: true,
|
|
||||||
),
|
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '首页背景渐变',
|
title: '首页背景渐变',
|
||||||
setKey: SettingBoxKey.enableGradientBg,
|
setKey: SettingBoxKey.enableGradientBg,
|
||||||
@@ -126,8 +116,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
divisions: 35,
|
divisions: 35,
|
||||||
suffix: 'dp',
|
suffix: 'dp',
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
maxRowWidth = result;
|
maxRowWidth = result;
|
||||||
setting.put(SettingBoxKey.maxRowWidth, result);
|
setting.put(SettingBoxKey.maxRowWidth, result);
|
||||||
@@ -137,13 +126,80 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
},
|
},
|
||||||
leading: const Icon(Icons.calendar_view_week_outlined),
|
leading: const Icon(Icons.calendar_view_week_outlined),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('列表宽度(dp)上限', style: titleStyle),
|
title: Text('列表宽度(dp)限制', style: titleStyle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前:${maxRowWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(context).size.width.toPrecision(2)}dp。'
|
'当前:${maxRowWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(context).size.width.toPrecision(2)}dp。'
|
||||||
'宽度越小列数越多,横条、大卡会2倍折算',
|
'宽度越小列数越多,横条、大卡会2倍折算',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '播放页移除安全边距',
|
||||||
|
subTitle: '隐藏状态栏、撑满屏幕,但播放控件仍处于安全域内',
|
||||||
|
leading: Icon(Icons.crop_outlined),
|
||||||
|
setKey: SettingBoxKey.videoPlayerRemoveSafeArea,
|
||||||
|
defaultVal: false,
|
||||||
|
needReboot: true,
|
||||||
|
),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '动态页启用瀑布流',
|
||||||
|
subTitle: '关闭会显示为单列',
|
||||||
|
leading: Icon(Icons.view_array_outlined),
|
||||||
|
setKey: SettingBoxKey.dynamicsWaterfallFlow,
|
||||||
|
defaultVal: true,
|
||||||
|
needReboot: true,
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
title: Text('动态页Up主显示位置', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.person_outlined),
|
||||||
|
subtitle: Text('当前:${upPanelPosition.labels}', style: subTitleStyle),
|
||||||
|
onTap: () async {
|
||||||
|
UpPanelPosition? result = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectDialog<UpPanelPosition>(
|
||||||
|
title: '动态页Up主显示位置',
|
||||||
|
value: upPanelPosition,
|
||||||
|
values: UpPanelPosition.values.map((e) {
|
||||||
|
return {'title': e.labels, 'value': e};
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
upPanelPosition = result;
|
||||||
|
setting.put(SettingBoxKey.upPanelPosition, result.code);
|
||||||
|
SmartDialog.showToast('重启生效');
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
onTap: () => settingController.setDynamicBadgeMode(context),
|
||||||
|
title: Text('动态未读标记', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.motion_photos_on_outlined),
|
||||||
|
subtitle: Obx(() => Text(
|
||||||
|
'当前标记样式:${settingController.dynamicBadgeType.value.description}',
|
||||||
|
style: subTitleStyle)),
|
||||||
|
),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '首页顶栏收起',
|
||||||
|
subTitle: '首页列表滑动时,收起顶栏',
|
||||||
|
leading: Icon(Icons.vertical_align_top_outlined),
|
||||||
|
setKey: SettingBoxKey.hideSearchBar,
|
||||||
|
defaultVal: false,
|
||||||
|
needReboot: true,
|
||||||
|
),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '首页底栏收起',
|
||||||
|
subTitle: '首页列表滑动时,收起底栏',
|
||||||
|
leading: Icon(Icons.vertical_align_bottom_outlined),
|
||||||
|
setKey: SettingBoxKey.hideTabBar,
|
||||||
|
defaultVal: false,
|
||||||
|
needReboot: true,
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -235,6 +291,9 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
leading: const Icon(Icons.opacity_outlined),
|
leading: const Icon(Icons.opacity_outlined),
|
||||||
title: Text('气泡提示不透明度', style: titleStyle),
|
title: Text('气泡提示不透明度', style: titleStyle),
|
||||||
subtitle: Text('自定义气泡提示(Toast)不透明度', style: subTitleStyle),
|
subtitle: Text('自定义气泡提示(Toast)不透明度', style: subTitleStyle),
|
||||||
|
trailing: Obx(() => Text(
|
||||||
|
settingController.toastOpacity.value.toStringAsFixed(1),
|
||||||
|
style: Theme.of(context).textTheme.titleSmall)),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@@ -263,15 +322,6 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
'当前模式:${settingController.themeType.value.description}',
|
'当前模式:${settingController.themeType.value.description}',
|
||||||
style: subTitleStyle)),
|
style: subTitleStyle)),
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
dense: false,
|
|
||||||
onTap: () => settingController.setDynamicBadgeMode(context),
|
|
||||||
title: Text('动态未读标记', style: titleStyle),
|
|
||||||
leading: const Icon(Icons.motion_photos_on_outlined),
|
|
||||||
subtitle: Obx(() => Text(
|
|
||||||
'当前标记样式:${settingController.dynamicBadgeType.value.description}',
|
|
||||||
style: subTitleStyle)),
|
|
||||||
),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/colorSetting'),
|
onTap: () => Get.toNamed('/colorSetting'),
|
||||||
@@ -281,13 +331,6 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
'当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}',
|
'当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}',
|
||||||
style: subTitleStyle)),
|
style: subTitleStyle)),
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
|
||||||
title: '默认展示评论区',
|
|
||||||
subTitle: '在视频详情页默认切换至评论区页',
|
|
||||||
leading: Icon(Icons.mode_comment_outlined),
|
|
||||||
setKey: SettingBoxKey.defaultShowComment,
|
|
||||||
defaultVal: false,
|
|
||||||
),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => settingController.seteDefaultHomePage(context),
|
onTap: () => settingController.seteDefaultHomePage(context),
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class SettingPage extends StatelessWidget {
|
|||||||
leading: const Icon(Icons.style_outlined),
|
leading: const Icon(Icons.style_outlined),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: const Text('外观设置'),
|
title: const Text('外观设置'),
|
||||||
subtitle: Text('横屏适配(平板)、列宽、首页、主题、字号、图片、动态红点、帧率等', style: subTitleStyle),
|
subtitle: Text('横屏适配(平板)、侧栏、列宽、首页、动态红点、主题、字号、图片、帧率等', style: subTitleStyle),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => Get.toNamed('/extraSetting'),
|
onTap: () => Get.toNamed('/extraSetting'),
|
||||||
@@ -81,7 +81,7 @@ class SettingPage extends StatelessWidget {
|
|||||||
visible: settingController.userLogin.value,
|
visible: settingController.userLogin.value,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.logout_outlined),
|
leading: const Icon(Icons.logout_outlined),
|
||||||
onTap: () => settingController.loginOut(),
|
onTap: () => settingController.loginOut(context),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: const Text('退出登录'),
|
title: const Text('退出登录'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -58,17 +58,12 @@ class _SubPageState extends State<SubPage> {
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
mainAxisExtent: Grid.calculateActualWidth(
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
context,
|
mainAxisExtent: 0),
|
||||||
Grid.maxRowWidth * 2,
|
|
||||||
StyleString.safeSpace) /
|
|
||||||
2.1 /
|
|
||||||
StyleString.aspectRatio),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount:
|
childCount:
|
||||||
_subController.subFolderData.value.list!.length,
|
_subController.subFolderData.value.list!.length,
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ class VideoDetailController extends GetxController
|
|||||||
type: DataSourceType.network,
|
type: DataSourceType.network,
|
||||||
httpHeaders: {
|
httpHeaders: {
|
||||||
'user-agent':
|
'user-agent':
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
|
||||||
'referer': HttpString.baseUrl
|
'referer': HttpString.baseUrl
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -329,7 +329,7 @@ class VideoDetailController extends GetxController
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
final List<VideoItem> allVideosList = data.dash!.video!;
|
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||||
print("allVideosList:${allVideosList}");
|
// print("allVideosList:${allVideosList}");
|
||||||
// 当前可播放的最高质量视频
|
// 当前可播放的最高质量视频
|
||||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||||
// 预设的画质为null,则当前可用的最高质量
|
// 预设的画质为null,则当前可用的最高质量
|
||||||
@@ -426,6 +426,7 @@ class VideoDetailController extends GetxController
|
|||||||
} else {
|
} else {
|
||||||
if (result['code'] == -404) {
|
if (result['code'] == -404) {
|
||||||
isShowCover.value = false;
|
isShowCover.value = false;
|
||||||
|
SmartDialog.showToast('视频不存在或已被删除');
|
||||||
}
|
}
|
||||||
if (result['code'] == 87008) {
|
if (result['code'] == 87008) {
|
||||||
SmartDialog.showToast("当前视频可能是专属视频,可能需包月充电观看(${result['msg']})");
|
SmartDialog.showToast("当前视频可能是专属视频,可能需包月充电观看(${result['msg']})");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@@ -42,6 +43,8 @@ class VideoIntroController extends GetxController {
|
|||||||
|
|
||||||
// 是否点赞
|
// 是否点赞
|
||||||
RxBool hasLike = false.obs;
|
RxBool hasLike = false.obs;
|
||||||
|
// 是否点踩
|
||||||
|
RxBool hasDislike = false.obs;
|
||||||
// 是否投币
|
// 是否投币
|
||||||
RxBool hasCoin = false.obs;
|
RxBool hasCoin = false.obs;
|
||||||
// 是否收藏
|
// 是否收藏
|
||||||
@@ -145,15 +148,16 @@ class VideoIntroController extends GetxController {
|
|||||||
// 获取点赞状态
|
// 获取点赞状态
|
||||||
Future queryHasLikeVideo() async {
|
Future queryHasLikeVideo() async {
|
||||||
var result = await VideoHttp.hasLikeVideo(bvid: bvid);
|
var result = await VideoHttp.hasLikeVideo(bvid: bvid);
|
||||||
// data num 被点赞标志 0:未点赞 1:已点赞
|
// data num 被点赞标志 0:未点赞 1:已点赞 2:已点踩
|
||||||
hasLike.value = result["data"] == 1 ? true : false;
|
hasLike.value = result["data"] == 1;
|
||||||
|
hasDislike.value = result["data"] == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取投币状态
|
// 获取投币状态
|
||||||
Future queryHasCoinVideo() async {
|
Future queryHasCoinVideo() async {
|
||||||
var result = await VideoHttp.hasCoinVideo(bvid: bvid);
|
var result = await VideoHttp.hasCoinVideo(bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
hasCoin.value = result["data"]['multiply'] == 0 ? false : true;
|
hasCoin.value = result["data"]['multiply'] != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +174,7 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 一键三连
|
// 一键三连
|
||||||
Future actionOneThree() async {
|
Future actionOneThree(BuildContext context) async {
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
@@ -180,19 +184,19 @@ class VideoIntroController extends GetxController {
|
|||||||
SmartDialog.showToast('🙏 UP已经收到了~');
|
SmartDialog.showToast('🙏 UP已经收到了~');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: const Text('一键三连 给UP送温暖'),
|
content: const Text('一键三连 给UP送温暖'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: const Text('点错了')),
|
child: const Text('点错了')),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
Get.back();
|
||||||
var result = await VideoHttp.oneThree(bvid: bvid);
|
var result = await VideoHttp.oneThree(bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
hasLike.value = result["data"]["like"];
|
hasLike.value = result["data"]["like"];
|
||||||
@@ -202,7 +206,6 @@ class VideoIntroController extends GetxController {
|
|||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast(result['msg']);
|
SmartDialog.showToast(result['msg']);
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
)
|
)
|
||||||
@@ -224,6 +227,7 @@ class VideoIntroController extends GetxController {
|
|||||||
if (!hasLike.value) {
|
if (!hasLike.value) {
|
||||||
SmartDialog.showToast('点赞成功');
|
SmartDialog.showToast('点赞成功');
|
||||||
hasLike.value = true;
|
hasLike.value = true;
|
||||||
|
hasDislike.value = false;
|
||||||
videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1;
|
videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1;
|
||||||
} else if (hasLike.value) {
|
} else if (hasLike.value) {
|
||||||
SmartDialog.showToast('取消赞');
|
SmartDialog.showToast('取消赞');
|
||||||
@@ -236,6 +240,29 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future actionDislikeVideo() async {
|
||||||
|
if (userInfo == null) {
|
||||||
|
SmartDialog.showToast('账号未登录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var result =
|
||||||
|
await VideoHttp.dislikeVideo(bvid: bvid, type: !hasDislike.value);
|
||||||
|
if (result['status']) {
|
||||||
|
// hasLike.value = result["data"] == 1 ? true : false;
|
||||||
|
if (!hasDislike.value) {
|
||||||
|
SmartDialog.showToast('点踩成功');
|
||||||
|
hasDislike.value = true;
|
||||||
|
hasLike.value = false;
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('取消踩');
|
||||||
|
hasDislike.value = false;
|
||||||
|
}
|
||||||
|
// hasDislike.refresh();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 投币
|
// 投币
|
||||||
Future actionCoinVideo() async {
|
Future actionCoinVideo() async {
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
@@ -350,10 +377,33 @@ class VideoIntroController extends GetxController {
|
|||||||
|
|
||||||
// 分享视频
|
// 分享视频
|
||||||
Future actionShareVideo() async {
|
Future actionShareVideo() async {
|
||||||
var result = await Share.share(
|
showDialog(
|
||||||
'${videoDetail.value.title} UP主: ${videoDetail.value.owner!.name!} - ${HttpString.baseUrl}/video/$bvid')
|
context: Get.context!,
|
||||||
|
builder: (context) {
|
||||||
|
String videoUrl = '${HttpString.baseUrl}/video/$bvid';
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('分享方式'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: videoUrl));
|
||||||
|
SmartDialog.showToast('已复制');
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
child: const Text('复制链接')),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var result = await Share.share('${videoDetail.value.title} '
|
||||||
|
'UP主: ${videoDetail.value.owner!.name!}'
|
||||||
|
' - $videoUrl')
|
||||||
.whenComplete(() {});
|
.whenComplete(() {});
|
||||||
|
Get.back();
|
||||||
return result;
|
return result;
|
||||||
|
},
|
||||||
|
child: const Text('分享视频')),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future queryVideoInFolder() async {
|
Future queryVideoInFolder() async {
|
||||||
@@ -394,7 +444,7 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关注/取关up
|
// 关注/取关up
|
||||||
Future actionRelationMod() async {
|
Future actionRelationMod(BuildContext context) async {
|
||||||
feedBack();
|
feedBack();
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
@@ -413,16 +463,15 @@ class VideoIntroController extends GetxController {
|
|||||||
actionStatus = 0;
|
actionStatus = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SmartDialog.show(
|
await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(currentStatus == 0 ? '关注UP主?' : '取消关注UP主?'),
|
content: Text(currentStatus == 0 ? '关注UP主?' : '取消关注UP主?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'点错了',
|
'点错了',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -465,7 +514,7 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData t = Theme.of(context);
|
final ThemeData t = Theme.of(context);
|
||||||
final Color outline = t.colorScheme.outline;
|
final Color outline = t.colorScheme.outline;
|
||||||
|
bool isHorizontal = context.width > context.height * 1.25;
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10),
|
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10),
|
||||||
@@ -252,20 +253,132 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
? Column(
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onPushMember,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 1, horizontal: 0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
type: 'avatar',
|
||||||
|
src: loadingStatus
|
||||||
|
? owner.face
|
||||||
|
: widget.videoDetail!.owner!.face,
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
fadeInDuration: Duration.zero,
|
||||||
|
fadeOutDuration: Duration.zero,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
owner.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: t.colorScheme.primary),
|
||||||
|
// semanticsLabel: "Up主:${owner.name}",
|
||||||
|
),
|
||||||
|
const SizedBox(height: 0),
|
||||||
|
Text(
|
||||||
|
follower,
|
||||||
|
semanticsLabel: "$follower粉丝",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
const Spacer(),
|
||||||
|
Obx(() => AnimatedOpacity(
|
||||||
|
opacity: loadingStatus ||
|
||||||
|
videoIntroController
|
||||||
|
.followStatus.isEmpty
|
||||||
|
? 0
|
||||||
|
: 1,
|
||||||
|
duration: const Duration(milliseconds: 50),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 32,
|
||||||
|
child: Obx(
|
||||||
|
() => videoIntroController
|
||||||
|
.followStatus.isNotEmpty
|
||||||
|
? TextButton(
|
||||||
|
onPressed: () =>
|
||||||
|
videoIntroController
|
||||||
|
.actionRelationMod(
|
||||||
|
context),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 8, right: 8),
|
||||||
|
foregroundColor:
|
||||||
|
followStatus['attribute'] !=
|
||||||
|
0
|
||||||
|
? outline
|
||||||
|
: t.colorScheme
|
||||||
|
.onPrimary,
|
||||||
|
backgroundColor:
|
||||||
|
followStatus['attribute'] !=
|
||||||
|
0
|
||||||
|
? t.colorScheme
|
||||||
|
.onInverseSurface
|
||||||
|
: t.colorScheme
|
||||||
|
.primary, // 设置按钮背景色
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
followStatus['attribute'] != 0
|
||||||
|
? '已关注'
|
||||||
|
: '关注',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: t.textTheme
|
||||||
|
.labelMedium!.fontSize),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: ElevatedButton(
|
||||||
|
onPressed: () =>
|
||||||
|
videoIntroController
|
||||||
|
.actionRelationMod(
|
||||||
|
context),
|
||||||
|
child: const Text('关注'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
if (isHorizontal)
|
||||||
|
Expanded(
|
||||||
|
child: actionGrid(context, videoIntroController)),
|
||||||
|
]),
|
||||||
|
const SizedBox(height: 8),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => showIntroDetail(),
|
onTap: () => showIntroDetail(),
|
||||||
|
child: Row(children: [
|
||||||
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
!loadingStatus
|
!loadingStatus
|
||||||
? widget.videoDetail!.title
|
? widget.videoDetail!.title
|
||||||
: videoItem['title'],
|
: videoItem['title'],
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 16,
|
||||||
|
color: t.colorScheme.outline,
|
||||||
),
|
),
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -363,7 +476,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// 点赞收藏转发 布局样式2
|
// 点赞收藏转发 布局样式2
|
||||||
actionGrid(context, videoIntroController),
|
if (!isHorizontal) actionGrid(context, videoIntroController),
|
||||||
// 合集
|
// 合集
|
||||||
if (!loadingStatus &&
|
if (!loadingStatus &&
|
||||||
widget.videoDetail!.ugcSeason != null) ...[
|
widget.videoDetail!.ugcSeason != null) ...[
|
||||||
@@ -387,93 +500,10 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
changeFuc: videoIntroController.changeSeasonOrbangu,
|
changeFuc: videoIntroController.changeSeasonOrbangu,
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
GestureDetector(
|
|
||||||
onTap: onPushMember,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8, horizontal: 4),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
type: 'avatar',
|
|
||||||
src: loadingStatus
|
|
||||||
? owner.face
|
|
||||||
: widget.videoDetail!.owner!.face,
|
|
||||||
width: 34,
|
|
||||||
height: 34,
|
|
||||||
fadeInDuration: Duration.zero,
|
|
||||||
fadeOutDuration: Duration.zero,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
owner.name,
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
// semanticsLabel: "Up主:${owner.name}",
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
follower,
|
|
||||||
semanticsLabel: "粉丝数:$follower",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: t.textTheme.labelSmall!.fontSize,
|
|
||||||
color: outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Obx(() => AnimatedOpacity(
|
|
||||||
opacity: loadingStatus ||
|
|
||||||
videoIntroController
|
|
||||||
.followStatus.isEmpty
|
|
||||||
? 0
|
|
||||||
: 1,
|
|
||||||
duration: const Duration(milliseconds: 50),
|
|
||||||
child: SizedBox(
|
|
||||||
height: 32,
|
|
||||||
child: Obx(
|
|
||||||
() => videoIntroController
|
|
||||||
.followStatus.isNotEmpty
|
|
||||||
? TextButton(
|
|
||||||
onPressed: videoIntroController
|
|
||||||
.actionRelationMod,
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 8, right: 8),
|
|
||||||
foregroundColor:
|
|
||||||
followStatus['attribute'] != 0
|
|
||||||
? outline
|
|
||||||
: t.colorScheme.onPrimary,
|
|
||||||
backgroundColor:
|
|
||||||
followStatus['attribute'] != 0
|
|
||||||
? t.colorScheme
|
|
||||||
.onInverseSurface
|
|
||||||
: t.colorScheme
|
|
||||||
.primary, // 设置按钮背景色
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
followStatus['attribute'] != 0
|
|
||||||
? '已关注'
|
|
||||||
: '关注',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: t.textTheme
|
|
||||||
.labelMedium!.fontSize),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: ElevatedButton(
|
|
||||||
onPressed: videoIntroController
|
|
||||||
.actionRelationMod,
|
|
||||||
child: const Text('关注'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: const SizedBox(
|
: const SizedBox(
|
||||||
height: 100,
|
height: 130,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
@@ -503,6 +533,16 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
? Utils.numFormat(widget.videoDetail!.stat!.like!)
|
? Utils.numFormat(widget.videoDetail!.stat!.like!)
|
||||||
: '-'),
|
: '-'),
|
||||||
),
|
),
|
||||||
|
Obx(
|
||||||
|
() => ActionItem(
|
||||||
|
icon: const Icon(FontAwesomeIcons.thumbsDown),
|
||||||
|
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
|
||||||
|
onTap: handleState(videoIntroController.actionDislikeVideo),
|
||||||
|
selectStatus: videoIntroController.hasDislike.value,
|
||||||
|
loadingStatus: loadingStatus,
|
||||||
|
semanticsLabel: '点踩',
|
||||||
|
text: "点踩"),
|
||||||
|
),
|
||||||
// ActionItem(
|
// ActionItem(
|
||||||
// icon: const Icon(FontAwesomeIcons.clock),
|
// icon: const Icon(FontAwesomeIcons.clock),
|
||||||
// onTap: () => videoIntroController.actionShareVideo(),
|
// onTap: () => videoIntroController.actionShareVideo(),
|
||||||
@@ -536,7 +576,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.comment),
|
icon: const Icon(FontAwesomeIcons.comment),
|
||||||
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
|
onTap: () => videoDetailCtr.tabCtr
|
||||||
|
.animateTo(videoDetailCtr.tabCtr.index == 1 ? 0 : 1),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
semanticsLabel: '评论',
|
semanticsLabel: '评论',
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class IntroDetail extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 100),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:PiliPalaX/common/widgets/animated_dialog.dart';
|
|||||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/overlay_pop.dart';
|
import 'package:PiliPalaX/common/widgets/overlay_pop.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/video_card_h.dart';
|
import 'package:PiliPalaX/common/widgets/video_card_h.dart';
|
||||||
|
import '../../../../common/constants.dart';
|
||||||
|
import '../../../../utils/grid.dart';
|
||||||
import './controller.dart';
|
import './controller.dart';
|
||||||
|
|
||||||
class RelatedVideoPanel extends StatefulWidget {
|
class RelatedVideoPanel extends StatefulWidget {
|
||||||
@@ -33,18 +35,26 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
return FutureBuilder(
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.all(StyleString.safeSpace),
|
||||||
|
sliver: FutureBuilder(
|
||||||
future: _futureBuilder,
|
future: _futureBuilder,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
}
|
}
|
||||||
if (snapshot.data!['status'] && snapshot.data != null) {
|
if (snapshot.data!['status'] && snapshot.hasData) {
|
||||||
RxList relatedVideoList = _releatedController.relatedVideoList;
|
RxList relatedVideoList = _releatedController.relatedVideoList;
|
||||||
// 请求成功
|
// 请求成功
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverList(
|
() => SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
if (index == relatedVideoList.length) {
|
if (index == relatedVideoList.length) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@@ -80,14 +90,20 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.3,
|
||||||
|
mainAxisExtent: 0),
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 5),
|
}, childCount: 5),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createPopupDialog(videoItem) {
|
OverlayEntry _createPopupDialog(videoItem) {
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
late bool enableVerticalExpand;
|
late bool enableVerticalExpand;
|
||||||
late bool autoPiP;
|
late bool autoPiP;
|
||||||
late bool pipNoDanmaku;
|
late bool pipNoDanmaku;
|
||||||
|
late bool removeSafeArea;
|
||||||
final Floating floating = Floating();
|
final Floating floating = Floating();
|
||||||
// 生命周期监听
|
// 生命周期监听
|
||||||
// late final AppLifecycleListener _lifecycleListener;
|
// late final AppLifecycleListener _lifecycleListener;
|
||||||
@@ -66,7 +67,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
RxBool isFullScreen = false.obs;
|
RxBool isFullScreen = false.obs;
|
||||||
late StreamSubscription<bool> fullScreenStatusListener;
|
late StreamSubscription<bool> fullScreenStatusListener;
|
||||||
late final MethodChannel onUserLeaveHintListener;
|
late final MethodChannel onUserLeaveHintListener;
|
||||||
StreamSubscription<Duration>? _bufferedListener;
|
// StreamSubscription<Duration>? _bufferedListener;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -97,6 +98,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
pipNoDanmaku = setting.get(SettingBoxKey.pipNoDanmaku, defaultValue: true);
|
pipNoDanmaku = setting.get(SettingBoxKey.pipNoDanmaku, defaultValue: true);
|
||||||
enableVerticalExpand =
|
enableVerticalExpand =
|
||||||
setting.get(SettingBoxKey.enableVerticalExpand, defaultValue: false);
|
setting.get(SettingBoxKey.enableVerticalExpand, defaultValue: false);
|
||||||
|
removeSafeArea = setting.get(SettingBoxKey.videoPlayerRemoveSafeArea,
|
||||||
|
defaultValue: false);
|
||||||
|
if (removeSafeArea) hideStatusBar();
|
||||||
videoSourceInit();
|
videoSourceInit();
|
||||||
appbarStreamListen();
|
appbarStreamListen();
|
||||||
// lifecycleListener();
|
// lifecycleListener();
|
||||||
@@ -146,6 +150,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
if (status == PlayerStatus.completed) {
|
if (status == PlayerStatus.completed) {
|
||||||
shutdownTimerService.handleWaitingFinished();
|
shutdownTimerService.handleWaitingFinished();
|
||||||
bool notExitFlag = false;
|
bool notExitFlag = false;
|
||||||
|
|
||||||
/// 顺序播放 列表循环
|
/// 顺序播放 列表循环
|
||||||
if (plPlayerController!.playRepeat != PlayRepeat.pause &&
|
if (plPlayerController!.playRepeat != PlayRepeat.pause &&
|
||||||
plPlayerController!.playRepeat != PlayRepeat.singleCycle) {
|
plPlayerController!.playRepeat != PlayRepeat.singleCycle) {
|
||||||
@@ -246,7 +251,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
AutoOrientation.portraitUpMode();
|
AutoOrientation.portraitUpMode();
|
||||||
}
|
}
|
||||||
shutdownTimerService.handleWaitingFinished();
|
shutdownTimerService.handleWaitingFinished();
|
||||||
_bufferedListener?.cancel();
|
// _bufferedListener?.cancel();
|
||||||
if (plPlayerController != null) {
|
if (plPlayerController != null) {
|
||||||
plPlayerController!.removeStatusLister(playerListener);
|
plPlayerController!.removeStatusLister(playerListener);
|
||||||
fullScreenStatusListener.cancel();
|
fullScreenStatusListener.cancel();
|
||||||
@@ -262,7 +267,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
@override
|
@override
|
||||||
// 离开当前页面时
|
// 离开当前页面时
|
||||||
void didPushNext() async {
|
void didPushNext() async {
|
||||||
_bufferedListener?.cancel();
|
// _bufferedListener?.cancel();
|
||||||
|
|
||||||
/// 开启
|
/// 开启
|
||||||
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)
|
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)
|
||||||
@@ -392,10 +397,57 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Widget manualPlayerWidget = Obx(
|
||||||
|
() => Visibility(
|
||||||
|
visible: videoDetailController.isShowCover.value &&
|
||||||
|
videoDetailController.isEffective.value,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: AppBar(
|
||||||
|
primary: false,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
tooltip: '稍后再看',
|
||||||
|
onPressed: () async {
|
||||||
|
var res = await UserHttp.toViewLater(
|
||||||
|
bvid: videoDetailController.bvid);
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.history_outlined),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 14)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 12,
|
||||||
|
bottom: 10,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: '播放',
|
||||||
|
onPressed: () => handlePlay(),
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/play.png',
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
Widget childWhenDisabled = SafeArea(
|
Widget childWhenDisabled = SafeArea(
|
||||||
top: MediaQuery.of(context).orientation == Orientation.portrait &&
|
top: !removeSafeArea &&
|
||||||
|
MediaQuery.of(context).orientation == Orientation.portrait &&
|
||||||
isFullScreen.value == true,
|
isFullScreen.value == true,
|
||||||
bottom: MediaQuery.of(context).orientation == Orientation.portrait &&
|
bottom: !removeSafeArea &&
|
||||||
|
MediaQuery.of(context).orientation == Orientation.portrait &&
|
||||||
isFullScreen.value == true,
|
isFullScreen.value == true,
|
||||||
left: false, //isFullScreen != true,
|
left: false, //isFullScreen != true,
|
||||||
right: false, //isFullScreen != true,
|
right: false, //isFullScreen != true,
|
||||||
@@ -404,17 +456,26 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
key: videoDetailController.scaffoldKey,
|
key: videoDetailController.scaffoldKey,
|
||||||
|
// backgroundColor: Colors.black,
|
||||||
|
appBar: removeSafeArea
|
||||||
|
? null
|
||||||
|
: AppBar(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
appBar: PreferredSize(
|
|
||||||
preferredSize: const Size.fromHeight(0),
|
|
||||||
child: AppBar(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
toolbarHeight: 0,
|
||||||
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.light),
|
||||||
|
),
|
||||||
|
// appBar: PreferredSize(
|
||||||
|
// preferredSize: const Size.fromHeight(0),
|
||||||
|
// child: AppBar(
|
||||||
|
// backgroundColor: Colors.transparent,
|
||||||
|
// elevation: 0,
|
||||||
// systemOverlayStyle: const SystemUiOverlayStyle(
|
// systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
// statusBarColor: Colors.transparent,
|
// statusBarColor: Colors.transparent,
|
||||||
// statusBarIconBrightness: Brightness.light),
|
// statusBarIconBrightness: Brightness.light),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
@@ -424,7 +485,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
// print(videoDetailController.tabCtr.index);
|
// print(videoDetailController.tabCtr.index);
|
||||||
if (enableVerticalExpand &&
|
if (enableVerticalExpand &&
|
||||||
plPlayerController?.direction.value == 'vertical') {
|
plPlayerController?.direction.value == 'vertical') {
|
||||||
videoheight = context.width * 5 / 4;
|
videoheight = context.width;
|
||||||
}
|
}
|
||||||
if (MediaQuery.of(context).orientation ==
|
if (MediaQuery.of(context).orientation ==
|
||||||
Orientation.landscape &&
|
Orientation.landscape &&
|
||||||
@@ -439,15 +500,17 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
!isFullScreen.value &&
|
!isFullScreen.value &&
|
||||||
isShowing &&
|
isShowing &&
|
||||||
mounted) {
|
mounted) {
|
||||||
showStatusBar();
|
if (!removeSafeArea) showStatusBar();
|
||||||
}
|
}
|
||||||
return SizedBox(
|
return Container(
|
||||||
|
color: Colors.black,
|
||||||
height: MediaQuery.of(context).orientation ==
|
height: MediaQuery.of(context).orientation ==
|
||||||
Orientation.landscape ||
|
Orientation.landscape ||
|
||||||
isFullScreen.value == true
|
isFullScreen.value == true
|
||||||
? MediaQuery.sizeOf(context).height -
|
? MediaQuery.sizeOf(context).height -
|
||||||
(MediaQuery.of(context).orientation ==
|
(MediaQuery.of(context).orientation ==
|
||||||
Orientation.landscape
|
Orientation.landscape ||
|
||||||
|
removeSafeArea
|
||||||
? 0
|
? 0
|
||||||
: MediaQuery.of(context).padding.top)
|
: MediaQuery.of(context).padding.top)
|
||||||
: videoheight,
|
: videoheight,
|
||||||
@@ -498,59 +561,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Obx(
|
manualPlayerWidget,
|
||||||
() => Visibility(
|
|
||||||
visible: videoDetailController
|
|
||||||
.isShowCover.value &&
|
|
||||||
videoDetailController
|
|
||||||
.isEffective.value,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: AppBar(
|
|
||||||
primary: false,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
elevation: 0,
|
|
||||||
scrolledUnderElevation: 0,
|
|
||||||
backgroundColor:
|
|
||||||
Colors.transparent,
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
tooltip: '稍后再看',
|
|
||||||
onPressed: () async {
|
|
||||||
var res = await UserHttp
|
|
||||||
.toViewLater(
|
|
||||||
bvid:
|
|
||||||
videoDetailController
|
|
||||||
.bvid);
|
|
||||||
SmartDialog.showToast(
|
|
||||||
res['msg']);
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.history_outlined),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 14)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 12,
|
|
||||||
bottom: 10,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '播放',
|
|
||||||
onPressed: () => handlePlay(),
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/play.png',
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@@ -629,15 +640,308 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Widget childWhenDisabledAlmostSquareInner = Obx(() {
|
||||||
|
if (enableVerticalExpand &&
|
||||||
|
plPlayerController?.direction.value == 'vertical') {
|
||||||
|
final double videoheight = context.height -
|
||||||
|
(removeSafeArea
|
||||||
|
? 0
|
||||||
|
: (MediaQuery.of(context).padding.top +
|
||||||
|
MediaQuery.of(context).padding.bottom));
|
||||||
|
final double videowidth = videoheight * 9 / 16;
|
||||||
|
return Row(children: [
|
||||||
|
SizedBox(
|
||||||
|
height: videoheight,
|
||||||
|
width: isFullScreen.value == true ? context.width : videowidth,
|
||||||
|
child: PopScope(
|
||||||
|
canPop: isFullScreen.value != true,
|
||||||
|
onPopInvoked: (bool didPop) {
|
||||||
|
if (isFullScreen.value == true) {
|
||||||
|
plPlayerController!.triggerFullScreen(status: false);
|
||||||
|
}
|
||||||
|
if (MediaQuery.of(context).orientation ==
|
||||||
|
Orientation.landscape &&
|
||||||
|
!horizontalScreen) {
|
||||||
|
verticalScreenForTwoSeconds();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Stack(children: <Widget>[
|
||||||
|
if (isShowing) plPlayer,
|
||||||
|
|
||||||
|
/// 关闭自动播放时 手动播放
|
||||||
|
if (!videoDetailController.autoPlay.value) ...<Widget>[
|
||||||
|
Obx(
|
||||||
|
() => Visibility(
|
||||||
|
visible: videoDetailController.isShowCover.value,
|
||||||
|
child: Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
handlePlay();
|
||||||
|
},
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
type: 'emote',
|
||||||
|
src: videoDetailController.videoItem['pic'],
|
||||||
|
width: videowidth,
|
||||||
|
height: videoheight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
manualPlayerWidget,
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
controller: videoDetailController.tabCtr,
|
||||||
|
children: <Widget>[
|
||||||
|
CustomScrollView(
|
||||||
|
key: const PageStorageKey<String>('简介'),
|
||||||
|
slivers: <Widget>[
|
||||||
|
if (videoDetailController.videoType ==
|
||||||
|
SearchType.video) ...[
|
||||||
|
const VideoIntroPanel(),
|
||||||
|
] else if (videoDetailController.videoType ==
|
||||||
|
SearchType.media_bangumi) ...[
|
||||||
|
Obx(() => BangumiIntroPanel(
|
||||||
|
cid: videoDetailController.cid.value)),
|
||||||
|
],
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Divider(
|
||||||
|
indent: 12,
|
||||||
|
endIndent: 12,
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.06),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const RelatedVideoPanel(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => VideoReplyPanel(
|
||||||
|
bvid: videoDetailController.bvid,
|
||||||
|
oid: videoDetailController.oid.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
final double videoheight = context.height / 2.5;
|
||||||
|
final double videowidth = context.width;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: videowidth,
|
||||||
|
height: isFullScreen.value == true
|
||||||
|
? context.height -
|
||||||
|
(removeSafeArea
|
||||||
|
? 0
|
||||||
|
: (MediaQuery.of(context).padding.top +
|
||||||
|
MediaQuery.of(context).padding.bottom))
|
||||||
|
: videoheight,
|
||||||
|
child: PopScope(
|
||||||
|
canPop: isFullScreen.value != true,
|
||||||
|
onPopInvoked: (bool didPop) {
|
||||||
|
if (isFullScreen.value == true) {
|
||||||
|
plPlayerController!.triggerFullScreen(status: false);
|
||||||
|
}
|
||||||
|
if (MediaQuery.of(context).orientation ==
|
||||||
|
Orientation.landscape &&
|
||||||
|
!horizontalScreen) {
|
||||||
|
verticalScreenForTwoSeconds();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Stack(children: <Widget>[
|
||||||
|
if (isShowing) plPlayer,
|
||||||
|
|
||||||
|
/// 关闭自动播放时 手动播放
|
||||||
|
if (!videoDetailController.autoPlay.value) ...<Widget>[
|
||||||
|
Obx(
|
||||||
|
() => Visibility(
|
||||||
|
visible: videoDetailController.isShowCover.value,
|
||||||
|
child: Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
handlePlay();
|
||||||
|
},
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
type: 'emote',
|
||||||
|
src: videoDetailController.videoItem['pic'],
|
||||||
|
width: videowidth,
|
||||||
|
height: videoheight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
manualPlayerWidget,
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: CustomScrollView(
|
||||||
|
key: PageStorageKey<String>('简介${videoDetailController.bvid}'),
|
||||||
|
slivers: <Widget>[
|
||||||
|
if (videoDetailController.videoType == SearchType.video) ...[
|
||||||
|
const VideoIntroPanel(),
|
||||||
|
const RelatedVideoPanel(),
|
||||||
|
] else if (videoDetailController.videoType ==
|
||||||
|
SearchType.media_bangumi) ...[
|
||||||
|
Obx(() =>
|
||||||
|
BangumiIntroPanel(cid: videoDetailController.cid.value)),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(
|
||||||
|
() => VideoReplyPanel(
|
||||||
|
bvid: videoDetailController.bvid,
|
||||||
|
oid: videoDetailController.oid.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
Widget childWhenDisabledLandscapeInner = Obx(() {
|
Widget childWhenDisabledLandscapeInner = Obx(() {
|
||||||
// 系数是以下三个方程(分别代表特定平板、折叠屏内屏、普通手机横屏尺寸)的近似解
|
if (enableVerticalExpand &&
|
||||||
// 820x+1180y+983.67z=450
|
plPlayerController?.direction.value == 'vertical') {
|
||||||
// 1812x+2176y+1985.68z=680
|
final double videoheight = context.height -
|
||||||
// 1080x+2340y+1589.72z=560
|
(removeSafeArea
|
||||||
final double videoheight = sqrt(context.height * context.width) * 12.555 -
|
? 0
|
||||||
context.height * 7.690 -
|
: (MediaQuery.of(context).padding.top +
|
||||||
context.width * 4.741;
|
MediaQuery.of(context).padding.bottom));
|
||||||
final double videowidth = videoheight * 16 / 9;
|
final double videowidth = videoheight * 9 / 16;
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: videoheight,
|
||||||
|
width: isFullScreen.value == true ? context.width : videowidth,
|
||||||
|
child: PopScope(
|
||||||
|
canPop: isFullScreen.value != true,
|
||||||
|
onPopInvoked: (bool didPop) {
|
||||||
|
if (isFullScreen.value == true) {
|
||||||
|
plPlayerController!.triggerFullScreen(status: false);
|
||||||
|
}
|
||||||
|
if (MediaQuery.of(context).orientation ==
|
||||||
|
Orientation.landscape &&
|
||||||
|
!horizontalScreen) {
|
||||||
|
verticalScreenForTwoSeconds();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
if (isShowing) plPlayer,
|
||||||
|
|
||||||
|
/// 关闭自动播放时 手动播放
|
||||||
|
if (!videoDetailController.autoPlay.value) ...<Widget>[
|
||||||
|
Obx(
|
||||||
|
() => Visibility(
|
||||||
|
visible: videoDetailController.isShowCover.value,
|
||||||
|
child: Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
handlePlay();
|
||||||
|
},
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
type: 'emote',
|
||||||
|
src: videoDetailController.videoItem['pic'],
|
||||||
|
width: videowidth,
|
||||||
|
height: videoheight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
manualPlayerWidget,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: CustomScrollView(
|
||||||
|
key: PageStorageKey<String>('简介${videoDetailController.bvid}'),
|
||||||
|
slivers: <Widget>[
|
||||||
|
if (videoDetailController.videoType == SearchType.video) ...[
|
||||||
|
const VideoIntroPanel(),
|
||||||
|
const RelatedVideoPanel(),
|
||||||
|
] else if (videoDetailController.videoType ==
|
||||||
|
SearchType.media_bangumi) ...[
|
||||||
|
Obx(() => BangumiIntroPanel(
|
||||||
|
cid: videoDetailController.cid.value)),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(
|
||||||
|
() => VideoReplyPanel(
|
||||||
|
bvid: videoDetailController.bvid,
|
||||||
|
oid: videoDetailController.oid.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]))
|
||||||
|
// Expanded(
|
||||||
|
// child: TabBarView(
|
||||||
|
// physics: const BouncingScrollPhysics(),
|
||||||
|
// controller: videoDetailController.tabCtr,
|
||||||
|
// children: <Widget>[
|
||||||
|
// CustomScrollView(
|
||||||
|
// key: const PageStorageKey<String>('简介'),
|
||||||
|
// slivers: <Widget>[
|
||||||
|
// if (videoDetailController.videoType ==
|
||||||
|
// SearchType.video) ...[
|
||||||
|
// const VideoIntroPanel(),
|
||||||
|
// ] else if (videoDetailController.videoType ==
|
||||||
|
// SearchType.media_bangumi) ...[
|
||||||
|
// Obx(() => BangumiIntroPanel(
|
||||||
|
// cid: videoDetailController.cid.value)),
|
||||||
|
// ],
|
||||||
|
// SliverToBoxAdapter(
|
||||||
|
// child: Divider(
|
||||||
|
// indent: 12,
|
||||||
|
// endIndent: 12,
|
||||||
|
// color: Theme.of(context).dividerColor.withOpacity(0.06),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// const RelatedVideoPanel(),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// Obx(
|
||||||
|
// () => VideoReplyPanel(
|
||||||
|
// bvid: videoDetailController.bvid,
|
||||||
|
// oid: videoDetailController.oid.value,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final double videowidth =
|
||||||
|
max(context.height / context.width * 1.04, 1 / 2) * context.width;
|
||||||
|
final double videoheight = videowidth * 9 / 16;
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
@@ -689,57 +993,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Obx(
|
manualPlayerWidget,
|
||||||
() => Visibility(
|
|
||||||
visible: videoDetailController
|
|
||||||
.isShowCover.value &&
|
|
||||||
videoDetailController.isEffective.value,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: AppBar(
|
|
||||||
primary: false,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
elevation: 0,
|
|
||||||
scrolledUnderElevation: 0,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
tooltip: '稍后再看',
|
|
||||||
onPressed: () async {
|
|
||||||
var res =
|
|
||||||
await UserHttp.toViewLater(
|
|
||||||
bvid:
|
|
||||||
videoDetailController
|
|
||||||
.bvid);
|
|
||||||
SmartDialog.showToast(
|
|
||||||
res['msg']);
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.history_outlined),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 14)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 12,
|
|
||||||
bottom: 10,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '播放',
|
|
||||||
onPressed: () => handlePlay(),
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/play.png',
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
@@ -750,8 +1004,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
? 0
|
? 0
|
||||||
: context.height -
|
: context.height -
|
||||||
videoheight -
|
videoheight -
|
||||||
MediaQuery.of(context).padding.top -
|
(removeSafeArea
|
||||||
MediaQuery.of(context).padding.bottom,
|
? 0
|
||||||
|
: (MediaQuery.of(context).padding.top +
|
||||||
|
MediaQuery.of(context).padding.bottom)),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
key: PageStorageKey<String>(
|
key: PageStorageKey<String>(
|
||||||
'简介${videoDetailController.bvid}'),
|
'简介${videoDetailController.bvid}'),
|
||||||
@@ -759,6 +1015,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
if (videoDetailController.videoType ==
|
if (videoDetailController.videoType ==
|
||||||
SearchType.video) ...[
|
SearchType.video) ...[
|
||||||
const VideoIntroPanel(),
|
const VideoIntroPanel(),
|
||||||
|
const RelatedVideoPanel(),
|
||||||
] else if (videoDetailController.videoType ==
|
] else if (videoDetailController.videoType ==
|
||||||
SearchType.media_bangumi) ...[
|
SearchType.media_bangumi) ...[
|
||||||
Obx(() => BangumiIntroPanel(
|
Obx(() => BangumiIntroPanel(
|
||||||
@@ -772,56 +1029,85 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
width: isFullScreen.value == true
|
width: isFullScreen.value == true
|
||||||
? 0
|
? 0
|
||||||
: (context.width -
|
: (context.width -
|
||||||
MediaQuery.of(context).padding.left -
|
videowidth -
|
||||||
MediaQuery.of(context).padding.right -
|
(removeSafeArea
|
||||||
videowidth),
|
? 0
|
||||||
|
: (MediaQuery.of(context).padding.left +
|
||||||
|
MediaQuery.of(context).padding.right))),
|
||||||
height: context.height -
|
height: context.height -
|
||||||
MediaQuery.of(context).padding.top -
|
(removeSafeArea
|
||||||
MediaQuery.of(context).padding.bottom,
|
? 0
|
||||||
child: TabBarView(
|
: (MediaQuery.of(context).padding.top +
|
||||||
physics: const BouncingScrollPhysics(),
|
MediaQuery.of(context).padding.bottom)),
|
||||||
controller: videoDetailController.tabCtr,
|
child:
|
||||||
children: <Widget>[
|
// TabBarView(
|
||||||
if (videoDetailController.videoType == SearchType.video)
|
// physics: const BouncingScrollPhysics(),
|
||||||
const CustomScrollView(
|
// controller: videoDetailController.tabCtr,
|
||||||
slivers: [
|
// children: <Widget>[
|
||||||
RelatedVideoPanel(),
|
// if (videoDetailController.videoType == SearchType.video)
|
||||||
],
|
// const CustomScrollView(
|
||||||
),
|
// slivers: [
|
||||||
|
// RelatedVideoPanel(),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
Obx(
|
Obx(
|
||||||
() => VideoReplyPanel(
|
() => VideoReplyPanel(
|
||||||
bvid: videoDetailController.bvid,
|
bvid: videoDetailController.bvid,
|
||||||
oid: videoDetailController.oid.value,
|
oid: videoDetailController.oid.value,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Widget childWhenDisabledLandscape = Container(
|
Widget childWhenDisabledLandscape = SafeArea(
|
||||||
color: Theme.of(context).colorScheme.background,
|
left: !removeSafeArea && isFullScreen.value != true,
|
||||||
child: SafeArea(
|
right: !removeSafeArea && isFullScreen.value != true,
|
||||||
left: isFullScreen.value != true,
|
top: !removeSafeArea,
|
||||||
right: isFullScreen.value != true,
|
bottom: !removeSafeArea,
|
||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
Scaffold(
|
Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
key: videoDetailController.scaffoldKey,
|
key: videoDetailController.scaffoldKey,
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Colors.black,
|
||||||
appBar: PreferredSize(
|
appBar: removeSafeArea
|
||||||
preferredSize: const Size.fromHeight(0),
|
? null
|
||||||
child: AppBar(
|
: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.black,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
// systemOverlayStyle: const SystemUiOverlayStyle(
|
toolbarHeight: 0,
|
||||||
// statusBarColor: Colors.transparent,
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
// statusBarIconBrightness: Brightness.dark),
|
statusBarIconBrightness: Brightness.light),
|
||||||
),
|
),
|
||||||
|
body: Container(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
child: childWhenDisabledLandscapeInner))
|
||||||
|
]));
|
||||||
|
Widget childWhenDisabledAlmostSquare = SafeArea(
|
||||||
|
left: !removeSafeArea && isFullScreen.value != true,
|
||||||
|
right: !removeSafeArea && isFullScreen.value != true,
|
||||||
|
top: !removeSafeArea,
|
||||||
|
bottom: !removeSafeArea,
|
||||||
|
child: Stack(children: [
|
||||||
|
Scaffold(
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
key: videoDetailController.scaffoldKey,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
appBar: removeSafeArea
|
||||||
|
? null
|
||||||
|
: AppBar(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
elevation: 0,
|
||||||
|
toolbarHeight: 0,
|
||||||
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.light),
|
||||||
),
|
),
|
||||||
body: childWhenDisabledLandscapeInner)
|
body: Container(
|
||||||
])));
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
child: childWhenDisabledAlmostSquareInner))
|
||||||
|
]));
|
||||||
Widget childWhenEnabled = Obx(
|
Widget childWhenEnabled = Obx(
|
||||||
() => !videoDetailController.autoPlay.value
|
() => !videoDetailController.autoPlay.value
|
||||||
? const SizedBox()
|
? const SizedBox()
|
||||||
@@ -851,7 +1137,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (!horizontalScreen) {
|
Widget autoChoose(Widget childWhenDisabled) {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
return PiPSwitcher(
|
return PiPSwitcher(
|
||||||
childWhenDisabled: childWhenDisabled,
|
childWhenDisabled: childWhenDisabled,
|
||||||
@@ -862,34 +1148,59 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
return childWhenDisabled;
|
return childWhenDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OrientationBuilder(
|
if (!horizontalScreen) {
|
||||||
builder: (BuildContext context, Orientation orientation) {
|
return autoChoose(childWhenDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
if (!isShowing) {
|
if (!isShowing) {
|
||||||
return ColoredBox(color: Theme.of(context).colorScheme.background);
|
return ColoredBox(color: Theme.of(context).colorScheme.background);
|
||||||
}
|
}
|
||||||
if (orientation == Orientation.landscape) {
|
if (constraints.maxWidth > constraints.maxHeight * 1.25) {
|
||||||
if (!horizontalScreen) {
|
// hideStatusBar();
|
||||||
hideStatusBar();
|
// videoDetailController.hiddenReplyReplyPanel();
|
||||||
videoDetailController.hiddenReplyReplyPanel();
|
return autoChoose(childWhenDisabledLandscape);
|
||||||
|
} else if (constraints.maxWidth * (9 / 16) <
|
||||||
|
(2 / 5) * constraints.maxHeight) {
|
||||||
|
if (!isFullScreen.value) {
|
||||||
|
if (!removeSafeArea) showStatusBar();
|
||||||
}
|
}
|
||||||
|
return autoChoose(childWhenDisabled);
|
||||||
} else {
|
} else {
|
||||||
if (!isFullScreen.value) {
|
if (!isFullScreen.value) {
|
||||||
showStatusBar();
|
if (!removeSafeArea) showStatusBar();
|
||||||
}
|
}
|
||||||
|
return autoChoose(childWhenDisabledAlmostSquare);
|
||||||
}
|
}
|
||||||
if (Platform.isAndroid) {
|
//
|
||||||
return PiPSwitcher(
|
// final Orientation orientation =
|
||||||
childWhenDisabled:
|
// constraints.maxWidth > constraints.maxHeight * 1.25
|
||||||
!horizontalScreen || orientation == Orientation.portrait
|
// ? Orientation.landscape
|
||||||
? childWhenDisabled
|
// : Orientation.portrait;
|
||||||
: childWhenDisabledLandscape,
|
// if (orientation == Orientation.landscape) {
|
||||||
childWhenEnabled: childWhenEnabled,
|
// if (!horizontalScreen) {
|
||||||
floating: floating,
|
// hideStatusBar();
|
||||||
);
|
// videoDetailController.hiddenReplyReplyPanel();
|
||||||
}
|
// }
|
||||||
return !horizontalScreen || orientation == Orientation.portrait
|
// } else {
|
||||||
? childWhenDisabled
|
// if (!isFullScreen.value) {
|
||||||
: childWhenDisabledLandscape;
|
// showStatusBar();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (Platform.isAndroid) {
|
||||||
|
// return PiPSwitcher(
|
||||||
|
// childWhenDisabled:
|
||||||
|
// !horizontalScreen || orientation == Orientation.portrait
|
||||||
|
// ? childWhenDisabled
|
||||||
|
// : childWhenDisabledLandscape,
|
||||||
|
// childWhenEnabled: childWhenEnabled,
|
||||||
|
// floating: floating,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return !horizontalScreen || orientation == Orientation.portrait
|
||||||
|
// ? childWhenDisabled
|
||||||
|
// : childWhenDisabledLandscape;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
// TODO: 支持更多类型和颜色的弹幕
|
// TODO: 支持更多类型和颜色的弹幕
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('发送弹幕(测试)'),
|
title: const Text('发送弹幕'),
|
||||||
content: StatefulBuilder(
|
content: StatefulBuilder(
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
return TextField(
|
return TextField(
|
||||||
@@ -733,6 +733,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
double danmakuDurationVal = widget.controller!.danmakuDurationVal;
|
double danmakuDurationVal = widget.controller!.danmakuDurationVal;
|
||||||
// 弹幕描边
|
// 弹幕描边
|
||||||
double strokeWidth = widget.controller!.strokeWidth;
|
double strokeWidth = widget.controller!.strokeWidth;
|
||||||
|
// 字体粗细
|
||||||
|
int fontWeight = widget.controller!.fontWeight;
|
||||||
|
|
||||||
final DanmakuController danmakuController =
|
final DanmakuController danmakuController =
|
||||||
widget.controller!.danmakuController!;
|
widget.controller!.danmakuController!;
|
||||||
@@ -762,7 +764,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
child: Center(child: Text('弹幕设置', style: titleStyle)),
|
child: Center(child: Text('弹幕设置', style: titleStyle)),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
Text('智能云屏蔽 $danmakuWeight 级'),
|
Text('智能云屏蔽 $danmakuWeight 级'),
|
||||||
|
const Spacer(),
|
||||||
|
TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
minimumSize: Size.zero,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
onPressed: () => Get.toNamed('/danmakuBlock'),
|
||||||
|
child: const Text("屏蔽管理"))
|
||||||
|
],
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 0,
|
top: 0,
|
||||||
@@ -909,6 +924,45 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Text('字体粗细 ${fontWeight + 1}(可能无法精确调节)'),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 0,
|
||||||
|
bottom: 6,
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
),
|
||||||
|
child: SliderTheme(
|
||||||
|
data: SliderThemeData(
|
||||||
|
trackShape: MSliderTrackShape(),
|
||||||
|
thumbColor: Theme.of(context).colorScheme.primary,
|
||||||
|
activeTrackColor: Theme.of(context).colorScheme.primary,
|
||||||
|
trackHeight: 10,
|
||||||
|
thumbShape: const RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 6.0),
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
min: 0,
|
||||||
|
max: 8,
|
||||||
|
value: fontWeight.toDouble(),
|
||||||
|
divisions: 9,
|
||||||
|
label: '${fontWeight + 1}',
|
||||||
|
onChanged: (double val) {
|
||||||
|
fontWeight = val.toInt();
|
||||||
|
widget.controller!.fontWeight = fontWeight;
|
||||||
|
widget.controller?.putDanmakuSettings();
|
||||||
|
setState(() {});
|
||||||
|
try {
|
||||||
|
final DanmakuOption currentOption =
|
||||||
|
danmakuController.option;
|
||||||
|
final DanmakuOption updatedOption =
|
||||||
|
currentOption.copyWith(fontWeight: fontWeight);
|
||||||
|
danmakuController.updateOption(updatedOption);
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Text('描边粗细 $strokeWidth'),
|
Text('描边粗细 $strokeWidth'),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
|
|||||||
@@ -213,7 +213,9 @@ class _WhisperPageState extends State<WhisperPage> {
|
|||||||
.content['title'] ??
|
.content['title'] ??
|
||||||
sessionList[i]
|
sessionList[i]
|
||||||
.lastMsg
|
.lastMsg
|
||||||
.content['reply_content'])
|
.content[
|
||||||
|
'reply_content']) ??
|
||||||
|
sessionList[i].lastMsg.content
|
||||||
: '不支持的消息类型',
|
: '不支持的消息类型',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ class PlPlayerController {
|
|||||||
late double opacityVal;
|
late double opacityVal;
|
||||||
late double fontSizeVal;
|
late double fontSizeVal;
|
||||||
late double strokeWidth;
|
late double strokeWidth;
|
||||||
|
late int fontWeight;
|
||||||
late double danmakuDurationVal;
|
late double danmakuDurationVal;
|
||||||
late List<double> speedsList;
|
late List<double> speedsList;
|
||||||
double? defaultDuration;
|
double? defaultDuration;
|
||||||
@@ -286,12 +287,10 @@ class PlPlayerController {
|
|||||||
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
||||||
danmakuWeight.value =
|
danmakuWeight.value =
|
||||||
setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0);
|
setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0);
|
||||||
blockTypes =
|
blockTypes = setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []);
|
||||||
setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []);
|
|
||||||
showArea = setting.get(SettingBoxKey.danmakuShowArea, defaultValue: 0.5);
|
showArea = setting.get(SettingBoxKey.danmakuShowArea, defaultValue: 0.5);
|
||||||
// 不透明度
|
// 不透明度
|
||||||
opacityVal =
|
opacityVal = setting.get(SettingBoxKey.danmakuOpacity, defaultValue: 1.0);
|
||||||
setting.get(SettingBoxKey.danmakuOpacity, defaultValue: 1.0);
|
|
||||||
// 字体大小
|
// 字体大小
|
||||||
fontSizeVal =
|
fontSizeVal =
|
||||||
setting.get(SettingBoxKey.danmakuFontScale, defaultValue: 1.0);
|
setting.get(SettingBoxKey.danmakuFontScale, defaultValue: 1.0);
|
||||||
@@ -300,6 +299,8 @@ class PlPlayerController {
|
|||||||
setting.get(SettingBoxKey.danmakuDuration, defaultValue: 4.0);
|
setting.get(SettingBoxKey.danmakuDuration, defaultValue: 4.0);
|
||||||
// 描边粗细
|
// 描边粗细
|
||||||
strokeWidth = setting.get(SettingBoxKey.strokeWidth, defaultValue: 1.5);
|
strokeWidth = setting.get(SettingBoxKey.strokeWidth, defaultValue: 1.5);
|
||||||
|
// 弹幕字体粗细
|
||||||
|
fontWeight = setting.get(SettingBoxKey.fontWeight, defaultValue: 5);
|
||||||
playRepeat = PlayRepeat.values.toList().firstWhere(
|
playRepeat = PlayRepeat.values.toList().firstWhere(
|
||||||
(e) =>
|
(e) =>
|
||||||
e.value ==
|
e.value ==
|
||||||
@@ -312,7 +313,7 @@ class PlPlayerController {
|
|||||||
.get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false);
|
.get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false);
|
||||||
// 后台播放
|
// 后台播放
|
||||||
_continuePlayInBackground.value =
|
_continuePlayInBackground.value =
|
||||||
setting.get(SettingBoxKey.continuePlayInBackground, defaultValue: true);
|
setting.get(SettingBoxKey.continuePlayInBackground, defaultValue: false);
|
||||||
if (!enableAutoLongPressSpeed) {
|
if (!enableAutoLongPressSpeed) {
|
||||||
_longPressSpeed.value = videoStorage
|
_longPressSpeed.value = videoStorage
|
||||||
.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 3.0);
|
.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 3.0);
|
||||||
@@ -458,7 +459,6 @@ class PlPlayerController {
|
|||||||
bufferSize: bufferSize,
|
bufferSize: bufferSize,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var pp = player.platform as NativePlayer;
|
var pp = player.platform as NativePlayer;
|
||||||
// 解除倍速限制
|
// 解除倍速限制
|
||||||
await pp.setProperty("af", "scaletempo2=max-speed=8");
|
await pp.setProperty("af", "scaletempo2=max-speed=8");
|
||||||
@@ -515,7 +515,7 @@ class PlPlayerController {
|
|||||||
configuration: VideoControllerConfiguration(
|
configuration: VideoControllerConfiguration(
|
||||||
enableHardwareAcceleration: enableHA,
|
enableHardwareAcceleration: enableHA,
|
||||||
androidAttachSurfaceAfterVideoParameters: false,
|
androidAttachSurfaceAfterVideoParameters: false,
|
||||||
hwdec: hwdec,
|
hwdec: enableHA ? hwdec: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -991,8 +991,7 @@ class PlPlayerController {
|
|||||||
|
|
||||||
/// 设置后台播放
|
/// 设置后台播放
|
||||||
Future<void> setBackgroundPlay(bool val) async {
|
Future<void> setBackgroundPlay(bool val) async {
|
||||||
_continuePlayInBackground.value = val;
|
setting.put(SettingBoxKey.enableBackgroundPlay, val);
|
||||||
setting.put(SettingBoxKey.continuePlayInBackground, val);
|
|
||||||
videoPlayerServiceHandler.revalidateSetting();
|
videoPlayerServiceHandler.revalidateSetting();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1150,6 +1149,7 @@ class PlPlayerController {
|
|||||||
setting.put(SettingBoxKey.danmakuFontScale, fontSizeVal);
|
setting.put(SettingBoxKey.danmakuFontScale, fontSizeVal);
|
||||||
setting.put(SettingBoxKey.danmakuDuration, danmakuDurationVal);
|
setting.put(SettingBoxKey.danmakuDuration, danmakuDurationVal);
|
||||||
setting.put(SettingBoxKey.strokeWidth, strokeWidth);
|
setting.put(SettingBoxKey.strokeWidth, strokeWidth);
|
||||||
|
setting.put(SettingBoxKey.fontWeight, fontWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dispose({String type = 'single'}) async {
|
Future<void> dispose({String type = 'single'}) async {
|
||||||
@@ -1161,6 +1161,7 @@ class PlPlayerController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_playerCount.value = 0;
|
_playerCount.value = 0;
|
||||||
|
pause();
|
||||||
try {
|
try {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timerForVolume?.cancel();
|
_timerForVolume?.cancel();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:PiliPalaX/pages/video/detail/introduction/controller.dart';
|
|||||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
import 'package:PiliPalaX/utils/id_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
import 'package:flutter_volume_controller/flutter_volume_controller.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';
|
||||||
@@ -17,6 +18,7 @@ import 'package:PiliPalaX/plugin/pl_player/models/fullscreen_mode.dart';
|
|||||||
import 'package:PiliPalaX/plugin/pl_player/utils.dart';
|
import 'package:PiliPalaX/plugin/pl_player/utils.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
import 'package:saver_gallery/saver_gallery.dart';
|
||||||
import 'package:screen_brightness/screen_brightness.dart';
|
import 'package:screen_brightness/screen_brightness.dart';
|
||||||
|
|
||||||
import '../../common/widgets/audio_video_progress_bar.dart';
|
import '../../common/widgets/audio_video_progress_bar.dart';
|
||||||
@@ -330,8 +332,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
episodes.addAll(pages);
|
episodes.addAll(pages);
|
||||||
changeFucCall = videoIntroController!.changeSeasonOrbangu;
|
changeFucCall = videoIntroController!.changeSeasonOrbangu;
|
||||||
} else if (isBangumi) {
|
} else if (isBangumi) {
|
||||||
episodes.addAll(bangumiIntroController!.bangumiDetail.value.episodes!);
|
episodes.addAll(bangumiIntroController!
|
||||||
changeFucCall = bangumiIntroController!.changeSeasonOrbangu;
|
.bangumiDetail.value.episodes!);
|
||||||
|
changeFucCall =
|
||||||
|
bangumiIntroController!.changeSeasonOrbangu;
|
||||||
}
|
}
|
||||||
ListSheet(
|
ListSheet(
|
||||||
episodes: episodes,
|
episodes: episodes,
|
||||||
@@ -500,11 +504,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
controls: NoVideoControls,
|
controls: NoVideoControls,
|
||||||
pauseUponEnteringBackgroundMode: !_.continuePlayInBackground.value,
|
pauseUponEnteringBackgroundMode: !_.continuePlayInBackground.value,
|
||||||
resumeUponEnteringForegroundMode: true,
|
resumeUponEnteringForegroundMode: true,
|
||||||
subtitleViewConfiguration: const SubtitleViewConfiguration(
|
// 字幕尺寸调节
|
||||||
|
subtitleViewConfiguration: SubtitleViewConfiguration(
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
padding: EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(24.0),
|
||||||
textScaleFactor: 1.0,
|
textScaleFactor: MediaQuery.textScaleFactorOf(context)),
|
||||||
),
|
|
||||||
fit: _.videoFit.value,
|
fit: _.videoFit.value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -848,10 +852,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
|
|
||||||
// 头部、底部控制条
|
// 头部、底部控制条
|
||||||
SafeArea(
|
Obx(
|
||||||
top: false,
|
|
||||||
bottom: false,
|
|
||||||
child: Obx(
|
|
||||||
() => Column(
|
() => Column(
|
||||||
children: [
|
children: [
|
||||||
if (widget.headerControl != null || _.headerControl != null)
|
if (widget.headerControl != null || _.headerControl != null)
|
||||||
@@ -879,7 +880,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
/// 进度条 live模式下禁用
|
/// 进度条 live模式下禁用
|
||||||
|
|
||||||
@@ -981,7 +981,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: FractionalTranslation(
|
child: FractionalTranslation(
|
||||||
translation: const Offset(1, 0.0),
|
translation: const Offset(1, -0.4),
|
||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: _.showControls.value,
|
visible: _.showControls.value,
|
||||||
child: ComBtn(
|
child: ComBtn(
|
||||||
@@ -1000,6 +1000,42 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// 截图
|
||||||
|
Obx(
|
||||||
|
() => Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FractionalTranslation(
|
||||||
|
translation: const Offset(-1, -0.4),
|
||||||
|
child: Visibility(
|
||||||
|
visible: _.showControls.value && _.isFullScreen.value,
|
||||||
|
child: ComBtn(
|
||||||
|
tooltip: '截图',
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.photo_camera,
|
||||||
|
size: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
fuc: () => {
|
||||||
|
_.videoPlayerController
|
||||||
|
?.screenshot(format: 'image/png')
|
||||||
|
.then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
SmartDialog.showToast('截图成功');
|
||||||
|
String _name = DateTime.now().toString();
|
||||||
|
SaverGallery.saveImage(value,
|
||||||
|
name: _name,
|
||||||
|
androidRelativePath: "Pictures/Screenshots",
|
||||||
|
androidExistNotSave: false);
|
||||||
|
SmartDialog.showToast('$_name.png已保存到相册/截图');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
//
|
//
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (_.dataStatus.loading || _.isBuffering.value) {
|
if (_.dataStatus.loading || _.isBuffering.value) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class AppBarAni extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
tileMode: TileMode.mirror,
|
tileMode: TileMode.mirror,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: child,
|
child: SafeArea(child: child),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:PiliPalaX/pages/setting/pages/logs.dart';
|
|||||||
|
|
||||||
import '../pages/about/index.dart';
|
import '../pages/about/index.dart';
|
||||||
import '../pages/blacklist/index.dart';
|
import '../pages/blacklist/index.dart';
|
||||||
|
import '../pages/danmaku_block/index.dart';
|
||||||
import '../pages/dynamics/detail/index.dart';
|
import '../pages/dynamics/detail/index.dart';
|
||||||
import '../pages/dynamics/index.dart';
|
import '../pages/dynamics/index.dart';
|
||||||
import '../pages/fan/index.dart';
|
import '../pages/fan/index.dart';
|
||||||
@@ -184,6 +185,8 @@ class Routes {
|
|||||||
CustomGetPage(name: '/subscription', page: () => const SubPage()),
|
CustomGetPage(name: '/subscription', page: () => const SubPage()),
|
||||||
// 订阅详情
|
// 订阅详情
|
||||||
CustomGetPage(name: '/subDetail', page: () => const SubDetailPage()),
|
CustomGetPage(name: '/subDetail', page: () => const SubDetailPage()),
|
||||||
|
// 弹幕屏蔽管理
|
||||||
|
CustomGetPage(name: '/danmakuBlock', page: () => const DanmakuBlockPage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class CacheManage {
|
class CacheManage {
|
||||||
CacheManage._internal();
|
CacheManage._internal();
|
||||||
@@ -76,17 +77,16 @@ class CacheManage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 清除缓存
|
// 清除缓存
|
||||||
Future<bool> clearCacheAll() async {
|
Future<bool> clearCacheAll(BuildContext context) async {
|
||||||
bool cleanStatus = await SmartDialog.show(
|
bool cleanStatus = await showDialog(
|
||||||
useSystem: true,
|
context: context,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
builder: (context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'),
|
content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: (() => {SmartDialog.dismiss()}),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -94,7 +94,7 @@ class CacheManage {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
SmartDialog.showLoading(msg: '正在清除...');
|
SmartDialog.showLoading(msg: '正在清除...');
|
||||||
try {
|
try {
|
||||||
// 清除缓存 图片缓存
|
// 清除缓存 图片缓存
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import 'dart:io';
|
|||||||
|
|
||||||
class DownloadUtils {
|
class DownloadUtils {
|
||||||
// 获取存储权限
|
// 获取存储权限
|
||||||
static Future<bool> requestStoragePer() async {
|
static Future<bool> requestStoragePer(BuildContext context) async {
|
||||||
await Permission.storage.request();
|
await Permission.storage.request();
|
||||||
PermissionStatus status = await Permission.storage.status;
|
PermissionStatus status = await Permission.storage.status;
|
||||||
if (status == PermissionStatus.denied ||
|
if (status == PermissionStatus.denied ||
|
||||||
status == PermissionStatus.permanentlyDenied) {
|
status == PermissionStatus.permanentlyDenied) {
|
||||||
SmartDialog.show(
|
if (!context.mounted) return false;
|
||||||
useSystem: true,
|
await showDialog(
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: const Text('存储权限未授权'),
|
content: const Text('存储权限未授权'),
|
||||||
@@ -69,21 +69,22 @@ class DownloadUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> checkPermissionDependOnSdkInt() async {
|
static Future<bool> checkPermissionDependOnSdkInt(BuildContext context) async {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
if (androidInfo.version.sdkInt <= 32) {
|
if (androidInfo.version.sdkInt <= 32) {
|
||||||
return await requestStoragePer();
|
if (!context.mounted) return false;
|
||||||
|
return await requestStoragePer(context);
|
||||||
} else {
|
} else {
|
||||||
return await requestPhotoPer();
|
return await requestPhotoPer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await requestStoragePer();
|
return await requestStoragePer(context);
|
||||||
}
|
}
|
||||||
static Future<bool> downloadImg(String imgUrl,
|
static Future<bool> downloadImg(BuildContext context, String imgUrl,
|
||||||
{String imgType = 'cover'}) async {
|
{String imgType = 'cover'}) async {
|
||||||
try {
|
try {
|
||||||
if (!await checkPermissionDependOnSdkInt()) {
|
if (!await checkPermissionDependOnSdkInt(context)) {
|
||||||
// // return false;
|
// // return false;
|
||||||
}
|
}
|
||||||
SmartDialog.showLoading(msg: '正在下载原图');
|
SmartDialog.showLoading(msg: '正在下载原图');
|
||||||
|
|||||||
@@ -1,18 +1,112 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'storage.dart';
|
import 'storage.dart';
|
||||||
class Grid {
|
class Grid {
|
||||||
static double maxRowWidth = GStrorage.setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double;
|
static double maxRowWidth = GStrorage.setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double;
|
||||||
|
//
|
||||||
|
// static double calculateActualWidth(BuildContext context, double maxCrossAxisExtent, double crossAxisSpacing, {double? screenWidthOffset}) {
|
||||||
|
// double screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
// if (screenWidthOffset != null) {
|
||||||
|
// screenWidth -= screenWidthOffset;
|
||||||
|
// }
|
||||||
|
// if (GStrorage.setting.get(SettingBoxKey.adaptiveNavBar, defaultValue: false) as bool) {
|
||||||
|
// screenWidth -= 55;
|
||||||
|
// }
|
||||||
|
// int columnCount = ((screenWidth - crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil();
|
||||||
|
// if (columnCount < 1){
|
||||||
|
// columnCount = 1;
|
||||||
|
// }
|
||||||
|
// double columnWidth = (screenWidth - crossAxisSpacing) ~/ columnCount - crossAxisSpacing;
|
||||||
|
// return columnWidth;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
class SliverGridDelegateWithExtentAndRatio extends SliverGridDelegate {
|
||||||
|
/// Creates a delegate that makes grid layouts with tiles that have a maximum
|
||||||
|
/// cross-axis extent.
|
||||||
|
///
|
||||||
|
/// The [maxCrossAxisExtent], [mainAxisExtent], [mainAxisSpacing],
|
||||||
|
/// and [crossAxisSpacing] arguments must not be negative.
|
||||||
|
/// The [childAspectRatio] argument must be greater than zero.
|
||||||
|
const SliverGridDelegateWithExtentAndRatio({
|
||||||
|
required this.maxCrossAxisExtent,
|
||||||
|
this.mainAxisSpacing = 0.0,
|
||||||
|
this.crossAxisSpacing = 0.0,
|
||||||
|
this.childAspectRatio = 1.0,
|
||||||
|
this.mainAxisExtent = 0.0,
|
||||||
|
}) : assert(maxCrossAxisExtent > 0),
|
||||||
|
assert(mainAxisSpacing >= 0),
|
||||||
|
assert(crossAxisSpacing >= 0),
|
||||||
|
assert(childAspectRatio > 0);
|
||||||
|
|
||||||
static double calculateActualWidth(BuildContext context, double maxCrossAxisExtent, double crossAxisSpacing, {double? screenWidthOffset}) {
|
/// The maximum extent of tiles in the cross axis.
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
///
|
||||||
if (screenWidthOffset != null) {
|
/// This delegate will select a cross-axis extent for the tiles that is as
|
||||||
screenWidth -= screenWidthOffset;
|
/// large as possible subject to the following conditions:
|
||||||
|
///
|
||||||
|
/// - The extent evenly divides the cross-axis extent of the grid.
|
||||||
|
/// - The extent is at most [maxCrossAxisExtent].
|
||||||
|
///
|
||||||
|
/// For example, if the grid is vertical, the grid is 500.0 pixels wide, and
|
||||||
|
/// [maxCrossAxisExtent] is 150.0, this delegate will create a grid with 4
|
||||||
|
/// columns that are 125.0 pixels wide.
|
||||||
|
final double maxCrossAxisExtent;
|
||||||
|
|
||||||
|
/// The number of logical pixels between each child along the main axis.
|
||||||
|
final double mainAxisSpacing;
|
||||||
|
|
||||||
|
/// The number of logical pixels between each child along the cross axis.
|
||||||
|
final double crossAxisSpacing;
|
||||||
|
|
||||||
|
/// The ratio of the cross-axis to the main-axis extent of each child.
|
||||||
|
final double childAspectRatio;
|
||||||
|
|
||||||
|
|
||||||
|
/// The extent of each tile in the main axis. If provided, it would add
|
||||||
|
/// after [childAspectRatio] is used.
|
||||||
|
final double mainAxisExtent;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool _debugAssertIsValid(double crossAxisExtent) {
|
||||||
|
assert(crossAxisExtent > 0.0);
|
||||||
|
assert(maxCrossAxisExtent > 0.0);
|
||||||
|
assert(mainAxisSpacing >= 0.0);
|
||||||
|
assert(crossAxisSpacing >= 0.0);
|
||||||
|
assert(childAspectRatio > 0.0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
int columnCount = ((screenWidth - crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil();
|
|
||||||
if (columnCount < 1){
|
@override
|
||||||
columnCount = 1;
|
SliverGridLayout getLayout(SliverConstraints constraints) {
|
||||||
|
assert(_debugAssertIsValid(constraints.crossAxisExtent));
|
||||||
|
int crossAxisCount = ((constraints.crossAxisExtent - crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil();
|
||||||
|
// Ensure a minimum count of 1, can be zero and result in an infinite extent
|
||||||
|
// below when the window size is 0.
|
||||||
|
crossAxisCount = max(1, crossAxisCount);
|
||||||
|
final double usableCrossAxisExtent = max(
|
||||||
|
0.0,
|
||||||
|
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1),
|
||||||
|
);
|
||||||
|
final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
|
||||||
|
final double childMainAxisExtent = childCrossAxisExtent / childAspectRatio + mainAxisExtent;
|
||||||
|
return SliverGridRegularTileLayout(
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
|
mainAxisStride: childMainAxisExtent + mainAxisSpacing,
|
||||||
|
crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
|
||||||
|
childMainAxisExtent: childMainAxisExtent,
|
||||||
|
childCrossAxisExtent: childCrossAxisExtent,
|
||||||
|
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
double columnWidth = (screenWidth - crossAxisSpacing) ~/ columnCount - crossAxisSpacing;
|
|
||||||
return columnWidth;
|
@override
|
||||||
|
bool shouldRelayout(SliverGridDelegateWithExtentAndRatio oldDelegate) {
|
||||||
|
return oldDelegate.maxCrossAxisExtent != maxCrossAxisExtent
|
||||||
|
|| oldDelegate.mainAxisSpacing != mainAxisSpacing
|
||||||
|
|| oldDelegate.crossAxisSpacing != crossAxisSpacing
|
||||||
|
|| oldDelegate.childAspectRatio != childAspectRatio
|
||||||
|
|| oldDelegate.mainAxisExtent != mainAxisExtent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,6 +145,7 @@ class SettingBoxKey {
|
|||||||
/// 其他
|
/// 其他
|
||||||
autoUpdate = 'autoUpdate',
|
autoUpdate = 'autoUpdate',
|
||||||
autoClearCache = 'autoClearCache',
|
autoClearCache = 'autoClearCache',
|
||||||
|
defaultShowComment = 'defaultShowComment',
|
||||||
replySortType = 'replySortType',
|
replySortType = 'replySortType',
|
||||||
defaultDynamicType = 'defaultDynamicType',
|
defaultDynamicType = 'defaultDynamicType',
|
||||||
enableHotKey = 'enableHotKey',
|
enableHotKey = 'enableHotKey',
|
||||||
@@ -156,7 +157,7 @@ class SettingBoxKey {
|
|||||||
disableLikeMsg = 'disableLikeMsg',
|
disableLikeMsg = 'disableLikeMsg',
|
||||||
defaultHomePage = 'defaultHomePage',
|
defaultHomePage = 'defaultHomePage',
|
||||||
|
|
||||||
// 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细
|
// 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 字体粗细
|
||||||
danmakuWeight = 'danmakuWeight',
|
danmakuWeight = 'danmakuWeight',
|
||||||
danmakuBlockType = 'danmakuBlockType',
|
danmakuBlockType = 'danmakuBlockType',
|
||||||
danmakuShowArea = 'danmakuShowArea',
|
danmakuShowArea = 'danmakuShowArea',
|
||||||
@@ -164,6 +165,8 @@ class SettingBoxKey {
|
|||||||
danmakuFontScale = 'danmakuFontScale',
|
danmakuFontScale = 'danmakuFontScale',
|
||||||
danmakuDuration = 'danmakuDuration',
|
danmakuDuration = 'danmakuDuration',
|
||||||
strokeWidth = 'strokeWidth',
|
strokeWidth = 'strokeWidth',
|
||||||
|
fontWeight = 'fontWeight',
|
||||||
|
danmakuFilterRule = 'danmakuFilterRule',
|
||||||
|
|
||||||
// 代理host port
|
// 代理host port
|
||||||
systemProxyHost = 'systemProxyHost',
|
systemProxyHost = 'systemProxyHost',
|
||||||
@@ -177,6 +180,9 @@ class SettingBoxKey {
|
|||||||
enableSingleRow = 'enableSingleRow', // 首页单列
|
enableSingleRow = 'enableSingleRow', // 首页单列
|
||||||
displayMode = 'displayMode',
|
displayMode = 'displayMode',
|
||||||
maxRowWidth = 'maxRowWidth', // 首页列最大宽度(dp)
|
maxRowWidth = 'maxRowWidth', // 首页列最大宽度(dp)
|
||||||
|
videoPlayerRemoveSafeArea = 'videoPlayerHideStatusBar',
|
||||||
|
dynamicsWaterfallFlow = 'dynamicsWaterfallFlow', // 动态瀑布流
|
||||||
|
upPanelPosition = 'upPanelPosition', // up主面板位置
|
||||||
adaptiveNavBar = 'adaptiveNavBar',
|
adaptiveNavBar = 'adaptiveNavBar',
|
||||||
enableMYBar = 'enableMYBar',
|
enableMYBar = 'enableMYBar',
|
||||||
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
||||||
@@ -184,8 +190,7 @@ class SettingBoxKey {
|
|||||||
tabbarSort = 'tabbarSort', // 首页tabbar
|
tabbarSort = 'tabbarSort', // 首页tabbar
|
||||||
dynamicBadgeMode = 'dynamicBadgeMode',
|
dynamicBadgeMode = 'dynamicBadgeMode',
|
||||||
hiddenSettingUnlocked = 'hiddenSettingUnlocked',
|
hiddenSettingUnlocked = 'hiddenSettingUnlocked',
|
||||||
enableGradientBg = 'enableGradientBg',
|
enableGradientBg = 'enableGradientBg';
|
||||||
defaultShowComment = 'defaultShowComment';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ class Utils {
|
|||||||
child: Text(
|
child: Text(
|
||||||
"点此查看完整更新(即commit)内容",
|
"点此查看完整更新(即commit)内容",
|
||||||
style:
|
style:
|
||||||
TextStyle(color: Theme.of(context).primaryColor),
|
TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -361,7 +361,7 @@ class Utils {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setting.put(SettingBoxKey.autoUpdate, false);
|
setting.put(SettingBoxKey.autoUpdate, false);
|
||||||
SmartDialog.dismiss();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'不再提醒',
|
'不再提醒',
|
||||||
@@ -370,7 +370,7 @@ class Utils {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style:
|
style:
|
||||||
|
|||||||
202
pubspec.lock
202
pubspec.lock
@@ -37,26 +37,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.10"
|
version: "3.5.1"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.5.0"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6
|
sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.2"
|
version: "1.5.3"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -69,10 +69,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: audio_service
|
name: audio_service
|
||||||
sha256: a4d989f1225ea9621898d60f23236dcbfc04876fa316086c23c5c4af075dbac4
|
sha256: "4547c312a94f9cb2c48b60823fb190767cbd63454a83c73049384d5d3cba4650"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.12"
|
version: "0.18.13"
|
||||||
audio_service_platform_interface:
|
audio_service_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -85,18 +85,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: audio_service_web
|
name: audio_service_web
|
||||||
sha256: "523e64ddc914c714d53eec2da85bba1074f08cf26c786d4efb322de510815ea7"
|
sha256: "9d7d5ae5f98a5727f2580fad73062f2484f400eef6cef42919413268e62a363e"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
version: "0.1.2"
|
||||||
audio_session:
|
audio_session:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: audio_session
|
name: audio_session
|
||||||
sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f"
|
sha256: a49af9981eec5d7cd73b37bacb6ee73f8143a6a9f9bd5b6021e6c346b9b6cf4e
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.18"
|
version: "0.1.19"
|
||||||
auto_orientation:
|
auto_orientation:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -149,10 +149,10 @@ packages:
|
|||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.8"
|
version: "2.4.9"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -173,10 +173,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6
|
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.0"
|
version: "8.9.2"
|
||||||
cached_network_image:
|
cached_network_image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -197,18 +197,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image_web
|
name: cached_network_image_web
|
||||||
sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316"
|
sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.2.0"
|
||||||
catcher_2:
|
catcher_2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: catcher_2
|
name: catcher_2
|
||||||
sha256: "0691d0a5a2b7ccbe434ff67071218bbff86d264d215c6afc10f404cd6d6e7a50"
|
sha256: "2c2c6f8cf8c817730cd1dbb010d55292396930e7a3d42c04c3039e3fd411a2f8"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.6"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -317,26 +317,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "1.0.8"
|
||||||
custom_sliding_segmented_control:
|
custom_sliding_segmented_control:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: custom_sliding_segmented_control
|
name: custom_sliding_segmented_control
|
||||||
sha256: "05b73fa48d57218bfdf806bad68a859812b216cd81fe81c6cbefde89f39eb257"
|
sha256: "8b29c39e053136cdb899cf1b049d11e5ac0f4622f6444ae4f80b6ba72a640763"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368"
|
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.4"
|
version: "2.3.6"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -365,10 +365,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3"
|
sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "5.4.3+1"
|
||||||
dio_cookie_manager:
|
dio_cookie_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -381,10 +381,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio_http2_adapter
|
name: dio_http2_adapter
|
||||||
sha256: "3bb35e81eb8a688eb1cb15beb97f46823698b44037e7b55227aa1060f5593adc"
|
sha256: ea2f5e7906a157cb049abce95f89a693459f82e5b376be4636086368e3a350f1
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.5.2"
|
||||||
dismissible_page:
|
dismissible_page:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -397,10 +397,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dynamic_color
|
name: dynamic_color
|
||||||
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
|
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.9"
|
version: "1.7.0"
|
||||||
easy_debounce:
|
easy_debounce:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -510,14 +510,6 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_adaptive_scaffold:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_adaptive_scaffold
|
|
||||||
sha256: "600bbe237530a249f957f7d0f36273c20bd38d137e28e098c5231c30cadbe927"
|
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.10+1"
|
|
||||||
flutter_cache_manager:
|
flutter_cache_manager:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -575,26 +567,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
|
sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.17"
|
version: "2.0.19"
|
||||||
flutter_smart_dialog:
|
flutter_smart_dialog:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_smart_dialog
|
name: flutter_smart_dialog
|
||||||
sha256: e9ee69eeac16165d142f1974b4db05ca9846cffafb7c94674a38ec07d7e6cda1
|
sha256: "9b23a0b23b52a259f2901997eaf0b169bf5c61ff2178204872709610e9f6c0be"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.9.6"
|
version: "4.9.6+1"
|
||||||
flutter_svg:
|
flutter_svg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_svg
|
name: flutter_svg
|
||||||
sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c
|
sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.9"
|
version: "2.0.10+1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -604,10 +596,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_volume_controller
|
name: flutter_volume_controller
|
||||||
sha256: "0f10cc759499cb6c3e152a8f6ff8e5ce385b99db7e1f586d1a29d8e6c11f4082"
|
sha256: fa4c36dfe7ef7f423704f34ab8e64e00b4a30a90aa6e56f251e9dba649efcd7f
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.2"
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -617,10 +609,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fluttertoast
|
name: fluttertoast
|
||||||
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
|
sha256: "81b68579e23fcbcada2db3d50302813d2371664afe6165bc78148050ab94bf66"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.2.4"
|
version: "8.2.5"
|
||||||
font_awesome_flutter:
|
font_awesome_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -633,10 +625,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: frontend_server_client
|
name: frontend_server_client
|
||||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "4.0.0"
|
||||||
get:
|
get:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -745,10 +737,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a"
|
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.6"
|
version: "4.1.7"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -777,10 +769,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.1"
|
version: "4.9.0"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -817,10 +809,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: logger
|
name: logger
|
||||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2+1"
|
version: "2.3.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -833,10 +825,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mailer
|
name: mailer
|
||||||
sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382"
|
sha256: d25d89555c1031abacb448f07b801d7c01b4c21d4558e944b12b64394c84a3cb
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.1"
|
version: "6.1.0"
|
||||||
marquee:
|
marquee:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -971,8 +963,8 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: master
|
ref: master
|
||||||
resolved-ref: d1cb3f0190ca67ec4d7fd372dac96f4f17a81a1a
|
resolved-ref: e12184de6f4ab4647d098c561b0508c47ce359e8
|
||||||
url: "https://github.com/guozhigq/flutter_ns_danmaku.git"
|
url: "https://github.com/orz12/flutter_ns_danmaku.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.5"
|
version: "0.0.5"
|
||||||
octo_image:
|
octo_image:
|
||||||
@@ -1027,18 +1019,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "2.2.4"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1075,26 +1067,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: permission_handler
|
name: permission_handler
|
||||||
sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6"
|
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.2.0"
|
version: "11.3.1"
|
||||||
permission_handler_android:
|
permission_handler_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_android
|
name: permission_handler_android
|
||||||
sha256: "758284a0976772f9c744d6384fc5dc4834aa61e3f7aa40492927f244767374eb"
|
sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.3"
|
version: "12.0.6"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_apple
|
name: permission_handler_apple
|
||||||
sha256: c6bf440f80acd2a873d3d91a699e4cc770f86e7e6b576dda98759e8b92b39830
|
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.3.0"
|
version: "9.4.4"
|
||||||
permission_handler_html:
|
permission_handler_html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1107,10 +1099,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_platform_interface
|
name: permission_handler_platform_interface
|
||||||
sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c"
|
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.2.1"
|
||||||
permission_handler_windows:
|
permission_handler_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1147,10 +1139,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.4"
|
version: "3.9.1"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1211,10 +1203,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: saver_gallery
|
name: saver_gallery
|
||||||
sha256: cceebad1f792adad4eb5015b415dcd521779a772ea574f0d7fc534b128deac83
|
sha256: "0f740608072053a0da3b19cc5812a87e36f5c3c0b959d2475c4eb3d697f4a782"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
screen_brightness:
|
screen_brightness:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1275,10 +1267,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sentry
|
name: sentry
|
||||||
sha256: a7946f4a90b0feb47214981d881b98149e05f6c576da9f2a2f33945bf561de25
|
sha256: fd1fbfe860c05f5c52820ec4dbf2b6473789e83ead26cfc18bca4fe80bf3f008
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.16.0"
|
version: "8.2.0"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1291,10 +1283,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956
|
sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.4.0"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1472,26 +1464,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
|
sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.4"
|
version: "6.2.6"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f"
|
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.2"
|
version: "6.3.1"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.4"
|
version: "6.2.5"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1504,18 +1496,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.2.0"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1544,26 +1536,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics
|
name: vector_graphics
|
||||||
sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172"
|
sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.9+2"
|
version: "1.1.11+1"
|
||||||
vector_graphics_codec:
|
vector_graphics_codec:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics_codec
|
name: vector_graphics_codec
|
||||||
sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d"
|
sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.9+2"
|
version: "1.1.11+1"
|
||||||
vector_graphics_compiler:
|
vector_graphics_compiler:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics_compiler
|
name: vector_graphics_compiler
|
||||||
sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad"
|
sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.9+2"
|
version: "1.1.11+1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1648,18 +1640,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: webview_flutter
|
name: webview_flutter
|
||||||
sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413
|
sha256: "4445dea609bcdf4bcf6b0521e8c936abb34711d9e9b73c5ff8a6fd4eecb0db3c"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.0"
|
version: "4.6.0"
|
||||||
webview_flutter_android:
|
webview_flutter_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: "4ea3c4e1b8ed590162b15b8a61b41b1ef3ff179a314627c16ce40c086d94b8af"
|
sha256: dad3313c9ead95517bb1cae5e1c9d20ba83729d5a59e5e83c0a2d66203f27f91
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.14.0"
|
version: "3.16.1"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1672,10 +1664,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_wkwebview
|
name: webview_flutter_wkwebview
|
||||||
sha256: "4d062ad505390ecef1c4bfb6001cd857a51e00912cc9dfb66edb1886a9ebd80c"
|
sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.2"
|
version: "3.13.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1717,5 +1709,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.2.0 <4.0.0"
|
dart: ">=3.2.3 <4.0.0"
|
||||||
flutter: ">=3.16.0"
|
flutter: ">=3.16.0"
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.5
|
cupertino_icons: ^1.0.5
|
||||||
# 动态取色
|
# 动态取色
|
||||||
dynamic_color: ^1.6.8
|
dynamic_color: ^1.6.8
|
||||||
# Adaptive scaffold
|
|
||||||
flutter_adaptive_scaffold: ^0.1.10+1
|
|
||||||
|
|
||||||
get: ^4.6.5
|
get: ^4.6.5
|
||||||
|
|
||||||
@@ -117,7 +115,7 @@ dependencies:
|
|||||||
# 弹幕
|
# 弹幕
|
||||||
ns_danmaku:
|
ns_danmaku:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/guozhigq/flutter_ns_danmaku.git
|
url: https://github.com/orz12/flutter_ns_danmaku.git
|
||||||
ref: master
|
ref: master
|
||||||
# 状态栏图标控制
|
# 状态栏图标控制
|
||||||
status_bar_control: ^3.2.1
|
status_bar_control: ^3.2.1
|
||||||
|
|||||||
Reference in New Issue
Block a user