Compare commits
29 Commits
v1.0.4.082
...
v1.0.5.082
Author | SHA1 | Date | |
---|---|---|---|
53d4379bb9 | |||
a928c575ef | |||
a0e51c86fc | |||
d670b8123a | |||
2621b096ac | |||
05631f7803 | |||
495ba57ca8 | |||
8990c4ae92 | |||
8bc6a32b06 | |||
6083578f93 | |||
aa7419f352 | |||
c90a6cd86c | |||
2e04c27292 | |||
5741f80536 | |||
161ba1c313 | |||
5d9dc6c1a9 | |||
1abe70d4d4 | |||
5fc959eb59 | |||
6322b29aef | |||
6461f72b5e | |||
e3d561bffd | |||
535cf69967 | |||
4314b0fc3c | |||
9da113726b | |||
201422c150 | |||
b67127123a | |||
3ce7578183 | |||
7b60cc2666 | |||
ead24e90fb |
18
README.md
18
README.md
@ -11,10 +11,11 @@
|
||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/174shots_so.png" width="32%" alt="home" />
|
||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/850shots_so.png" width="32%" alt="home" />
|
||||
<br/>
|
||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/main_screen.png" width="96%" alt="home" />
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
### 开发环境
|
||||
## 开发环境
|
||||
Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
||||
|
||||
```bash
|
||||
@ -30,7 +31,7 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
||||
|
||||
<br/>
|
||||
|
||||
### 功能
|
||||
## 功能
|
||||
|
||||
目前着重移动端(Android、iOS),暂时没有适配桌面端、Pad端、手表端等
|
||||
|
||||
@ -98,17 +99,18 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
||||
- [x] 图片质量设定
|
||||
- [x] 主题模式:亮色/暗色/跟随系统
|
||||
- [x] 震动反馈(可选)
|
||||
- [x] 高帧率
|
||||
- [ ] 等等
|
||||
|
||||
<br/>
|
||||
|
||||
### 下载
|
||||
## 下载
|
||||
|
||||
可以通过右侧release进行下载或拉取代码到本地进行编译
|
||||
|
||||
<br/>
|
||||
|
||||
### 声明
|
||||
## 声明
|
||||
|
||||
此项目(PiliPala)是个人为了兴趣而开发, 仅用于学习和测试。
|
||||
所用API皆从官方网站收集, 不提供任何破解内容。
|
||||
@ -117,7 +119,13 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
||||
|
||||
<br/>
|
||||
|
||||
### 致谢
|
||||
## 技术交流
|
||||
|
||||
Telegram https://t.me/+lm_oOVmF0RJiODk1
|
||||
|
||||
<br/>
|
||||
|
||||
## 致谢
|
||||
|
||||
- [bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect)
|
||||
- [flutter_meedu_videoplayer](https://github.com/zezo357/flutter_meedu_videoplayer)
|
||||
|
BIN
assets/sreenshot/main_screen.png
Normal file
BIN
assets/sreenshot/main_screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
30
change_log/1.0.5.0826.md
Normal file
30
change_log/1.0.5.0826.md
Normal file
@ -0,0 +1,30 @@
|
||||
## 1.0.5
|
||||
|
||||
主要是bug修复跟一部分小功能,弹幕功能需要下一版。
|
||||
问题反馈请前往QQ频道或提交issues。
|
||||
感谢🙏酷友「无力感*」「斤斤计较呀」「Pseudopamine」
|
||||
|
||||
### 新功能
|
||||
+ 高帧率支持
|
||||
+ 默认评论排序设置
|
||||
+ 默认动态类别设置
|
||||
+ 动态合集查看
|
||||
+ 同时观看人数
|
||||
+ iOS路由切换效果
|
||||
|
||||
|
||||
### 修复
|
||||
+ 收藏夹翻页
|
||||
+ 首页搜索框频繁点击消失
|
||||
+ 评论排序切换空白
|
||||
+ 快速返回首页
|
||||
+ 重复进入个人中心页面数据未刷新
|
||||
+ 动态goods数据异常
|
||||
+ 大会员切换番剧
|
||||
+ 高画质codes匹配
|
||||
|
||||
|
||||
### 优化
|
||||
+ 倍速选择
|
||||
+ 播放器亮度记忆
|
||||
+ 下载对应版本apk
|
@ -288,4 +288,8 @@ class Api {
|
||||
// github 获取最新版
|
||||
static const String latestApp =
|
||||
'https://api.github.com/repos/guozhigq/pilipala/releases/latest';
|
||||
|
||||
// 多少人在看
|
||||
// https://api.bilibili.com/x/player/online/total?aid=913663681&cid=1203559746&bvid=BV1MM4y1s7NZ&ts=56427838
|
||||
static const String onlineTotal = '/x/player/online/total';
|
||||
}
|
||||
|
@ -22,10 +22,18 @@ class DynamicsHttp {
|
||||
}
|
||||
var res = await Request().get(Api.followDynamic, data: data);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': DynamicsDataModel.fromJson(res.data['data']),
|
||||
};
|
||||
try {
|
||||
return {
|
||||
'status': true,
|
||||
'data': DynamicsDataModel.fromJson(res.data['data']),
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
'status': false,
|
||||
'data': [],
|
||||
'msg': err.toString(),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
|
@ -51,10 +51,17 @@ class UserHttp {
|
||||
'up_mid': mid,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
FavFolderData data = FavFolderData.fromJson(res.data['data']);
|
||||
return {'status': true, 'data': data};
|
||||
late FavFolderData data;
|
||||
if (res.data['data'] != null) {
|
||||
data = FavFolderData.fromJson(res.data['data']);
|
||||
return {'status': true, 'data': data};
|
||||
}
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': '账号未登录'};
|
||||
return {
|
||||
'status': false,
|
||||
'data': [],
|
||||
'msg': res.data['message'] ?? '账号未登录'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,4 +399,16 @@ class VideoHttp {
|
||||
return {'status': false, 'msg': res.data['result']['toast']};
|
||||
}
|
||||
}
|
||||
|
||||
// 查看视频同时在看人数
|
||||
static Future onlineTotal({int? aid, String? bvid, int? cid}) async {
|
||||
var res = await Request().get(Api.onlineTotal, data: {
|
||||
'aid': aid,
|
||||
'bvid': bvid,
|
||||
'cid': cid,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,13 @@ void main() async {
|
||||
await Request.setCookie();
|
||||
await Data.init();
|
||||
await GStrorage.lazyInit();
|
||||
// 小白条、导航栏沉浸
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.transparent,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
statusBarColor: Colors.transparent,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -360,7 +360,7 @@ class GoodItem {
|
||||
|
||||
String? brief;
|
||||
String? cover;
|
||||
String? id;
|
||||
dynamic id;
|
||||
String? jumpDesc;
|
||||
String? jumpUrl;
|
||||
String? name;
|
||||
|
@ -93,26 +93,19 @@ extension AudioQualityDesc on AudioQuality {
|
||||
}
|
||||
|
||||
enum VideoDecodeFormats {
|
||||
DVH1,
|
||||
AV1,
|
||||
HEVC,
|
||||
AVC,
|
||||
}
|
||||
|
||||
extension VideoDecodeFormatsDesc on VideoDecodeFormats {
|
||||
static final List<String> _descList = [
|
||||
'AV1',
|
||||
'HEVC',
|
||||
'AVC',
|
||||
];
|
||||
static final List<String> _descList = ['DVH1', 'AV1', 'HEVC', 'AVC'];
|
||||
get description => _descList[index];
|
||||
}
|
||||
|
||||
extension VideoDecodeFormatsCode on VideoDecodeFormats {
|
||||
static final List<String> _codeList = [
|
||||
'av01',
|
||||
'hev1',
|
||||
'avc1',
|
||||
];
|
||||
static final List<String> _codeList = ['dvh1', 'av01', 'hev1', 'avc1'];
|
||||
get code => _codeList[index];
|
||||
|
||||
static VideoDecodeFormats? fromCode(String code) {
|
||||
|
@ -47,6 +47,11 @@ class _AboutPageState extends State<AboutPage> {
|
||||
'PiliPala',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'使用Flutter开发的哔哩哔哩第三方客户端',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Obx(
|
||||
() => ListTile(
|
||||
@ -82,16 +87,6 @@ class _AboutPageState extends State<AboutPage> {
|
||||
height: 30,
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
title: const Text('作者'),
|
||||
trailing: Text('guozhigq', style: subTitleStyle),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
title: const Text('酷安'),
|
||||
trailing: Text('影若风', style: subTitleStyle),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => _aboutController.githubUrl(),
|
||||
title: const Text('Github'),
|
||||
@ -123,6 +118,11 @@ class _AboutPageState extends State<AboutPage> {
|
||||
title: const Text('TG频道'),
|
||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => _aboutController.aPay(),
|
||||
title: const Text('赞助'),
|
||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||
),
|
||||
Divider(
|
||||
thickness: 8,
|
||||
height: 30,
|
||||
@ -141,11 +141,12 @@ class AboutController extends GetxController {
|
||||
late LatestDataModel remoteAppInfo;
|
||||
RxBool isUpdate = true.obs;
|
||||
RxBool isLoading = true.obs;
|
||||
late LatestDataModel data;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
init();
|
||||
// init();
|
||||
// 获取当前版本
|
||||
getCurrentApp();
|
||||
// 获取最新的版本
|
||||
@ -153,16 +154,16 @@ class AboutController extends GetxController {
|
||||
}
|
||||
|
||||
// 获取设备信息
|
||||
Future init() async {
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
if (Platform.isAndroid) {
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
print(androidInfo.supportedAbis);
|
||||
} else if (Platform.isIOS) {
|
||||
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
|
||||
print(iosInfo);
|
||||
}
|
||||
}
|
||||
// Future init() async {
|
||||
// DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
// if (Platform.isAndroid) {
|
||||
// AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
// print(androidInfo.supportedAbis);
|
||||
// } else if (Platform.isIOS) {
|
||||
// IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
|
||||
// print(iosInfo);
|
||||
// }
|
||||
// }
|
||||
|
||||
// 获取当前版本
|
||||
Future getCurrentApp() async {
|
||||
@ -173,7 +174,7 @@ class AboutController extends GetxController {
|
||||
// 获取远程版本
|
||||
Future getRemoteApp() async {
|
||||
var result = await Request().get(Api.latestApp);
|
||||
LatestDataModel data = LatestDataModel.fromJson(result.data);
|
||||
data = LatestDataModel.fromJson(result.data);
|
||||
remoteAppInfo = data;
|
||||
remoteVersion.value = data.tagName!;
|
||||
isUpdate.value =
|
||||
@ -183,15 +184,7 @@ class AboutController extends GetxController {
|
||||
|
||||
// 跳转下载/本地更新
|
||||
Future onUpdate() async {
|
||||
// final dir = await getApplicationSupportDirectory();
|
||||
// final path = '${dir.path}/pilipala.apk';
|
||||
// var result = await Request()
|
||||
// .downloadFile(remoteAppInfo.assets!.first.downloadUrl, path);
|
||||
// print(result);
|
||||
launchUrl(
|
||||
Uri.parse('https://github.com/guozhigq/pilipala/releases'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
Utils.matchVersion(data);
|
||||
}
|
||||
|
||||
// 跳转github
|
||||
@ -242,4 +235,16 @@ class AboutController extends GetxController {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
aPay() {
|
||||
try {
|
||||
launchUrl(
|
||||
Uri.parse(
|
||||
'alipayqr://platformapi/startapp?saId=10000007&qrcode=https://qr.alipay.com/fkx14623ddwl1ping3ddd73'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/models/bangumi/info.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class BangumiPanel extends StatefulWidget {
|
||||
final List<EpisodeItem> pages;
|
||||
@ -24,12 +26,20 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
||||
late int currentIndex;
|
||||
final ScrollController listViewScrollCtr = ScrollController();
|
||||
final ScrollController listViewScrollCtr_2 = ScrollController();
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
dynamic userInfo;
|
||||
// 默认未开通
|
||||
int vipStatus = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
currentIndex = widget.pages.indexWhere((e) => e.cid == widget.cid!);
|
||||
scrollToIndex();
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
if (userInfo != null) {
|
||||
vipStatus = userInfo.vipStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -126,7 +136,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
||||
}
|
||||
|
||||
void changeFucCall(item, i) async {
|
||||
if (item.badge != null) {
|
||||
if (item.badge != null && vipStatus != 1) {
|
||||
SmartDialog.showToast('需要大会员');
|
||||
return;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import 'package:pilipala/models/dynamics/result.dart';
|
||||
import 'package:pilipala/models/dynamics/up.dart';
|
||||
import 'package:pilipala/models/live/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
@ -50,17 +51,21 @@ class DynamicsController extends GetxController {
|
||||
},
|
||||
];
|
||||
bool flag = false;
|
||||
RxInt initialValue = 1.obs;
|
||||
RxInt initialValue = 0.obs;
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
RxBool userLogin = false.obs;
|
||||
var userInfo;
|
||||
RxBool isLoadingDynamic = false.obs;
|
||||
Box setting = GStrorage.setting;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
userLogin.value = userInfo != null;
|
||||
super.onInit();
|
||||
initialValue.value =
|
||||
setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0);
|
||||
dynamicsType = DynamicsType.values[initialValue.value].obs;
|
||||
}
|
||||
|
||||
Future queryFollowDynamic({type = 'init'}) async {
|
||||
@ -99,7 +104,7 @@ class DynamicsController extends GetxController {
|
||||
}
|
||||
|
||||
onSelectType(value) async {
|
||||
dynamicsType.value = filterTypeList[value - 1]['value'];
|
||||
dynamicsType.value = filterTypeList[value]['value'];
|
||||
dynamicsList.value = [DynamicItemModel()];
|
||||
page = 1;
|
||||
initialValue.value = value;
|
||||
@ -109,16 +114,21 @@ class DynamicsController extends GetxController {
|
||||
|
||||
pushDetail(item, floor, {action = 'all'}) async {
|
||||
feedBack();
|
||||
|
||||
/// 点击评论action 直接查看评论
|
||||
if (action == 'comment') {
|
||||
Get.toNamed('/dynamicDetail',
|
||||
arguments: {'item': item, 'floor': floor, 'action': action});
|
||||
return false;
|
||||
}
|
||||
switch (item!.type) {
|
||||
/// 转发的动态
|
||||
case 'DYNAMIC_TYPE_FORWARD':
|
||||
Get.toNamed('/dynamicDetail',
|
||||
arguments: {'item': item, 'floor': floor});
|
||||
break;
|
||||
|
||||
/// 图文动态查看
|
||||
case 'DYNAMIC_TYPE_DRAW':
|
||||
Get.toNamed('/dynamicDetail',
|
||||
arguments: {'item': item, 'floor': floor});
|
||||
@ -134,6 +144,8 @@ class DynamicsController extends GetxController {
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
break;
|
||||
|
||||
/// 专栏文章查看
|
||||
case 'DYNAMIC_TYPE_ARTICLE':
|
||||
String title = item.modules.moduleDynamic.major.opus.title;
|
||||
String url = item.modules.moduleDynamic.major.opus.jumpUrl;
|
||||
@ -144,7 +156,10 @@ class DynamicsController extends GetxController {
|
||||
break;
|
||||
case 'DYNAMIC_TYPE_PGC':
|
||||
print('番剧');
|
||||
SmartDialog.showToast('暂未支持的类型,请联系开发者');
|
||||
break;
|
||||
|
||||
/// 纯文字动态查看
|
||||
case 'DYNAMIC_TYPE_WORD':
|
||||
print('纯文本');
|
||||
Get.toNamed('/dynamicDetail',
|
||||
@ -168,10 +183,19 @@ class DynamicsController extends GetxController {
|
||||
});
|
||||
break;
|
||||
|
||||
/// TODO
|
||||
/// 合集查看
|
||||
case 'DYNAMIC_TYPE_UGC_SEASON':
|
||||
print('合集');
|
||||
DynamicArchiveModel ugcSeason =
|
||||
item.modules.moduleDynamic.major.ugcSeason;
|
||||
int aid = ugcSeason.aid!;
|
||||
String bvid = IdUtils.av2bv(aid);
|
||||
String cover = ugcSeason.cover!;
|
||||
int cid = await SearchHttp.ab2c(bvid: bvid);
|
||||
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
||||
arguments: {'pic': cover, 'heroTag': bvid});
|
||||
break;
|
||||
|
||||
/// 番剧查看
|
||||
case 'DYNAMIC_TYPE_PGC_UNION':
|
||||
print('DYNAMIC_TYPE_PGC_UNION 番剧');
|
||||
DynamicArchiveModel pgc = item.modules.moduleDynamic.major.pgc;
|
||||
@ -247,7 +271,7 @@ class DynamicsController extends GetxController {
|
||||
void resetSearch() {
|
||||
mid.value = -1;
|
||||
dynamicsType.value = DynamicsType.values[0];
|
||||
initialValue.value = 1;
|
||||
initialValue.value = 0;
|
||||
SmartDialog.showToast('还原默认加载');
|
||||
dynamicsList.value = [DynamicItemModel()];
|
||||
queryFollowDynamic();
|
||||
|
@ -1,8 +1,10 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/http/reply.dart';
|
||||
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class DynamicDetailController extends GetxController {
|
||||
DynamicDetailController(this.oid, this.type);
|
||||
@ -16,9 +18,10 @@ class DynamicDetailController extends GetxController {
|
||||
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
|
||||
RxInt acount = 0.obs;
|
||||
|
||||
ReplySortType sortType = ReplySortType.time;
|
||||
ReplySortType _sortType = ReplySortType.time;
|
||||
RxString sortTypeTitle = ReplySortType.time.titles.obs;
|
||||
RxString sortTypeLabel = ReplySortType.time.labels.obs;
|
||||
Box setting = GStrorage.setting;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@ -29,6 +32,11 @@ class DynamicDetailController extends GetxController {
|
||||
acount.value =
|
||||
int.parse(item!.modules!.moduleStat!.comment!.count ?? '0');
|
||||
}
|
||||
int deaultReplySortIndex =
|
||||
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
|
||||
_sortType = ReplySortType.values[deaultReplySortIndex];
|
||||
sortTypeTitle.value = _sortType.titles;
|
||||
sortTypeLabel.value = _sortType.labels;
|
||||
}
|
||||
|
||||
Future queryReplyList({reqType = 'init'}) async {
|
||||
@ -39,7 +47,7 @@ class DynamicDetailController extends GetxController {
|
||||
oid: oid!,
|
||||
pageNum: currentPage + 1,
|
||||
type: type!,
|
||||
sort: sortType.index,
|
||||
sort: _sortType.index,
|
||||
);
|
||||
if (res['status']) {
|
||||
List<ReplyItemModel> replies = res['data'].replies;
|
||||
@ -76,20 +84,20 @@ class DynamicDetailController extends GetxController {
|
||||
// 排序搜索评论
|
||||
queryBySort() {
|
||||
feedBack();
|
||||
switch (sortType) {
|
||||
switch (_sortType) {
|
||||
case ReplySortType.time:
|
||||
sortType = ReplySortType.like;
|
||||
_sortType = ReplySortType.like;
|
||||
break;
|
||||
case ReplySortType.like:
|
||||
sortType = ReplySortType.reply;
|
||||
_sortType = ReplySortType.reply;
|
||||
break;
|
||||
case ReplySortType.reply:
|
||||
sortType = ReplySortType.time;
|
||||
_sortType = ReplySortType.time;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
sortTypeTitle.value = sortType.titles;
|
||||
sortTypeLabel.value = sortType.labels;
|
||||
sortTypeTitle.value = _sortType.titles;
|
||||
sortTypeLabel.value = _sortType.labels;
|
||||
replyList.clear();
|
||||
queryReplyList(reqType: 'init');
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
initialValue:
|
||||
_dynamicsController.initialValue.value,
|
||||
children: {
|
||||
1: Text(
|
||||
0: Text(
|
||||
'全部',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
@ -140,19 +140,19 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
.labelMedium!
|
||||
.fontSize),
|
||||
),
|
||||
2: Text('投稿',
|
||||
1: Text('投稿',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize)),
|
||||
3: Text('番剧',
|
||||
2: Text('番剧',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize)),
|
||||
4: Text('专栏',
|
||||
3: Text('专栏',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
|
@ -1,3 +1,5 @@
|
||||
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:pilipala/http/user.dart';
|
||||
@ -6,23 +8,45 @@ import 'package:pilipala/models/user/info.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class FavController extends GetxController {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
Rx<FavFolderData> favFolderData = FavFolderData().obs;
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
UserInfoData? userInfo;
|
||||
int currentPage = 1;
|
||||
int pageSize = 10;
|
||||
RxBool hasMore = true.obs;
|
||||
|
||||
Future<dynamic> queryFavFolder() async {
|
||||
Future<dynamic> queryFavFolder({type = 'init'}) async {
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
if (userInfo == null) {
|
||||
return {'status': false, 'msg': '账号未登录'};
|
||||
}
|
||||
if (!hasMore.value) {
|
||||
return;
|
||||
}
|
||||
var res = await await UserHttp.userfavFolder(
|
||||
pn: 1,
|
||||
ps: 10,
|
||||
pn: currentPage,
|
||||
ps: pageSize,
|
||||
mid: userInfo!.mid!,
|
||||
);
|
||||
if (res['status']) {
|
||||
favFolderData.value = res['data'];
|
||||
if (type == 'init') {
|
||||
favFolderData.value = res['data'];
|
||||
} else {
|
||||
if (res['data'].list.isNotEmpty) {
|
||||
favFolderData.value.list!.addAll(res['data'].list);
|
||||
favFolderData.update((val) {});
|
||||
}
|
||||
}
|
||||
hasMore.value = res['data'].hasMore;
|
||||
currentPage++;
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future onLoad() async {
|
||||
queryFavFolder(type: 'onload');
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
@ -14,11 +15,23 @@ class FavPage extends StatefulWidget {
|
||||
class _FavPageState extends State<FavPage> {
|
||||
final FavController _favController = Get.put(FavController());
|
||||
late Future _futureBuilderFuture;
|
||||
late ScrollController scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_futureBuilderFuture = _favController.queryFavFolder();
|
||||
scrollController = _favController.scrollController;
|
||||
scrollController.addListener(
|
||||
() {
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 300) {
|
||||
EasyThrottle.throttle('history', const Duration(seconds: 1), () {
|
||||
_favController.onLoad();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -40,6 +53,7 @@ class _FavPageState extends State<FavPage> {
|
||||
if (data['status']) {
|
||||
return Obx(
|
||||
() => ListView.builder(
|
||||
controller: scrollController,
|
||||
itemCount: _favController.favFolderData.value.list!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return FavItem(
|
||||
|
@ -142,7 +142,8 @@ class _ArchivePanelState extends State<ArchivePanel>
|
||||
}
|
||||
|
||||
class LoadMoreListSource extends LoadingMoreBase<VListItemModel> {
|
||||
final ArchiveController _archiveController = Get.put(ArchiveController());
|
||||
final ArchiveController _archiveController =
|
||||
Get.put(ArchiveController(), tag: Get.arguments['heroTag']);
|
||||
|
||||
@override
|
||||
Future<bool> loadData([bool isloadMoreAction = false]) async {
|
||||
|
@ -118,7 +118,8 @@ class _MemberDynamicPanelState extends State<MemberDynamicPanel>
|
||||
}
|
||||
|
||||
class LoadMoreListSource extends LoadingMoreBase<DynamicItemModel> {
|
||||
final _dynamicController = Get.put(MemberDynamicPanelController());
|
||||
final _dynamicController =
|
||||
Get.put(MemberDynamicPanelController(), tag: Get.arguments['heroTag']);
|
||||
|
||||
@override
|
||||
Future<bool> loadData([bool isloadMoreAction = false]) async {
|
||||
|
@ -45,11 +45,7 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
||||
return OpenContainer(
|
||||
closedElevation: 0,
|
||||
openElevation: 0,
|
||||
onClosed: (_) async {
|
||||
// 在 openBuilder 关闭时触发的回调函数
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
_searchController.onClear();
|
||||
},
|
||||
onClosed: (_) => _searchController.onClear(),
|
||||
openColor: Theme.of(context).colorScheme.background,
|
||||
middleColor: Theme.of(context).colorScheme.background,
|
||||
closedColor: Theme.of(context).colorScheme.background,
|
||||
|
@ -1,4 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/models/common/dynamics_type.dart';
|
||||
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
import 'widgets/switch_item.dart';
|
||||
@ -11,8 +14,28 @@ class ExtraSetting extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ExtraSettingState extends State<ExtraSetting> {
|
||||
Box setting = GStrorage.setting;
|
||||
late dynamic defaultReplySort;
|
||||
late dynamic defaultDynamicType;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 默认优先显示最新评论
|
||||
defaultReplySort =
|
||||
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
|
||||
// 优先展示全部动态 all
|
||||
defaultDynamicType =
|
||||
setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
|
||||
TextStyle subTitleStyle = Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: false,
|
||||
@ -23,8 +46,58 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
||||
),
|
||||
),
|
||||
body: ListView(
|
||||
children: const [
|
||||
SetSwitchItem(
|
||||
children: [
|
||||
ListTile(
|
||||
dense: false,
|
||||
title: Text('评论展示', style: titleStyle),
|
||||
subtitle: Text(
|
||||
'当前优先展示「${ReplySortType.values[defaultReplySort].titles}」',
|
||||
style: subTitleStyle,
|
||||
),
|
||||
trailing: PopupMenuButton(
|
||||
initialValue: defaultReplySort,
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
||||
onSelected: (item) {
|
||||
defaultReplySort = item;
|
||||
setting.put(SettingBoxKey.replySortType, item);
|
||||
setState(() {});
|
||||
},
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
for (var i in ReplySortType.values) ...[
|
||||
PopupMenuItem(
|
||||
value: i.index,
|
||||
child: Text(i.titles),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
dense: false,
|
||||
title: Text('动态展示', style: titleStyle),
|
||||
subtitle: Text(
|
||||
'当前优先展示「${DynamicsType.values[defaultDynamicType].labels}」',
|
||||
style: subTitleStyle,
|
||||
),
|
||||
trailing: PopupMenuButton(
|
||||
initialValue: defaultDynamicType,
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
||||
onSelected: (item) {
|
||||
defaultDynamicType = item;
|
||||
setting.put(SettingBoxKey.defaultDynamicType, item);
|
||||
setState(() {});
|
||||
},
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
for (var i in DynamicsType.values) ...[
|
||||
PopupMenuItem(
|
||||
value: i.index,
|
||||
child: Text(i.labels),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: '检查更新',
|
||||
subTitle: '每次启动时检查是否需要更新',
|
||||
setKey: SettingBoxKey.autoUpdate,
|
||||
|
94
lib/pages/setting/pages/display_mode.dart
Normal file
94
lib/pages/setting/pages/display_mode.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
|
||||
class SetDiaplayMode extends StatefulWidget {
|
||||
const SetDiaplayMode({super.key});
|
||||
|
||||
@override
|
||||
State<SetDiaplayMode> createState() => _SetDiaplayModeState();
|
||||
}
|
||||
|
||||
class _SetDiaplayModeState extends State<SetDiaplayMode> {
|
||||
List<DisplayMode> modes = <DisplayMode>[];
|
||||
DisplayMode? active;
|
||||
DisplayMode? preferred;
|
||||
|
||||
final ValueNotifier<int> page = ValueNotifier<int>(0);
|
||||
late final PageController controller = PageController()
|
||||
..addListener(() {
|
||||
page.value = controller.page!.round();
|
||||
});
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
init();
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
fetchAll();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> fetchAll() async {
|
||||
preferred = await FlutterDisplayMode.preferred;
|
||||
active = await FlutterDisplayMode.active;
|
||||
// GStorage().setDisplayModeType(preferred!);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
try {
|
||||
modes = await FlutterDisplayMode.supported;
|
||||
} on PlatformException catch (e) {
|
||||
print(e);
|
||||
}
|
||||
// var res = await GStorage().getDisplayModeType();
|
||||
// preferred = modes.toList().firstWhere((el) => el == res);
|
||||
FlutterDisplayMode.setPreferredMode(preferred!);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('屏幕帧率设置')),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (modes.isEmpty) const Text('Nothing here'),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 25, top: 10, bottom: 5),
|
||||
child: Text(
|
||||
'没有生效?重启app试试',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: modes.length,
|
||||
itemBuilder: (_, int i) {
|
||||
final DisplayMode mode = modes[i];
|
||||
return RadioListTile<DisplayMode>(
|
||||
value: mode,
|
||||
title: mode == DisplayMode.auto
|
||||
? const Text('自动')
|
||||
: Text('$mode${mode == active ? " [系统]" : ""}'),
|
||||
groupValue: preferred,
|
||||
onChanged: (DisplayMode? newMode) async {
|
||||
await FlutterDisplayMode.setPreferredMode(newMode!);
|
||||
await Future<dynamic>.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
await fetchAll();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -66,6 +66,18 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||
setKey: SettingBoxKey.enableHA,
|
||||
defaultVal: true,
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: '观看人数',
|
||||
subTitle: '展示同时在看人数',
|
||||
setKey: SettingBoxKey.enableOnlineTotal,
|
||||
defaultVal: false,
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: '亮度记忆',
|
||||
subTitle: '返回时自动调整视频亮度',
|
||||
setKey: SettingBoxKey.enableAutoBrightness,
|
||||
defaultVal: false,
|
||||
),
|
||||
ListTile(
|
||||
dense: false,
|
||||
title: Text('默认画质', style: titleStyle),
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
@ -5,6 +7,7 @@ import 'package:pilipala/models/common/theme_type.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'widgets/switch_item.dart';
|
||||
|
||||
class StyleSetting extends StatefulWidget {
|
||||
const StyleSetting({super.key});
|
||||
@ -66,6 +69,12 @@ class _StyleSettingState extends State<StyleSetting> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: 'iOS路由切换',
|
||||
subTitle: 'iOS路由切换样式,需重启',
|
||||
setKey: SettingBoxKey.iosTransition,
|
||||
defaultVal: false,
|
||||
),
|
||||
ListTile(
|
||||
dense: false,
|
||||
onTap: () {
|
||||
@ -188,12 +197,21 @@ class _StyleSettingState extends State<StyleSetting> {
|
||||
dense: false,
|
||||
onTap: () => Get.toNamed('/colorSetting'),
|
||||
title: Text('应用主题', style: titleStyle),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
||||
),
|
||||
ListTile(
|
||||
dense: false,
|
||||
onTap: () => Get.toNamed('/fontSizeSetting'),
|
||||
title: Text('字体大小', style: titleStyle),
|
||||
)
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
||||
),
|
||||
if (Platform.isAndroid)
|
||||
ListTile(
|
||||
dense: false,
|
||||
onTap: () => Get.toNamed('/displayModeSetting'),
|
||||
title: Text('屏幕帧率', style: titleStyle),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -32,6 +32,15 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
||||
val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false);
|
||||
}
|
||||
|
||||
void switchChange(value) {
|
||||
val = value ?? !val;
|
||||
Setting.put(widget.setKey, val);
|
||||
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
|
||||
Utils.checkUpdata();
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
|
||||
@ -41,9 +50,7 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline);
|
||||
return ListTile(
|
||||
enableFeedback: true,
|
||||
onTap: () {
|
||||
Setting.put(widget.setKey, !val);
|
||||
},
|
||||
onTap: () => switchChange(null),
|
||||
title: Text(widget.title!, style: titleStyle),
|
||||
subtitle: widget.subTitle != null
|
||||
? Text(widget.subTitle!, style: subTitleStyle)
|
||||
@ -51,22 +58,16 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
||||
trailing: Transform.scale(
|
||||
scale: 0.8,
|
||||
child: Switch(
|
||||
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.isNotEmpty && states.first == MaterialState.selected) {
|
||||
return const Icon(Icons.done);
|
||||
}
|
||||
return null; // All other states will use the default thumbIcon.
|
||||
}),
|
||||
value: val,
|
||||
onChanged: (value) {
|
||||
val = value;
|
||||
Setting.put(widget.setKey, value);
|
||||
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
|
||||
Utils.checkUpdata();
|
||||
}
|
||||
setState(() {});
|
||||
}),
|
||||
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.isNotEmpty && states.first == MaterialState.selected) {
|
||||
return const Icon(Icons.done);
|
||||
}
|
||||
return null; // All other states will use the default thumbIcon.
|
||||
}),
|
||||
value: val,
|
||||
onChanged: (val) => switchChange(val),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
|
||||
class VideoDetailController extends GetxController
|
||||
with GetSingleTickerProviderStateMixin {
|
||||
@ -68,6 +69,8 @@ class VideoDetailController extends GetxController
|
||||
late String videoUrl;
|
||||
late String audioUrl;
|
||||
late Duration defaultST;
|
||||
// 亮度
|
||||
double? brightness;
|
||||
// 默认记录历史记录
|
||||
bool enableHeart = true;
|
||||
var userInfo;
|
||||
@ -137,8 +140,17 @@ class VideoDetailController extends GetxController
|
||||
/// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl
|
||||
List<VideoItem> videoList =
|
||||
data.dash!.video!.where((i) => i.id == currentVideoQa.code).toList();
|
||||
firstVideo = videoList
|
||||
.firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code));
|
||||
try {
|
||||
firstVideo = videoList
|
||||
.firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code));
|
||||
} catch (_) {
|
||||
// 当前格式不可用
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get(
|
||||
SettingBoxKey.defaultDecode,
|
||||
defaultValue: VideoDecodeFormats.values.last.code))!;
|
||||
firstVideo = videoList
|
||||
.firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code));
|
||||
}
|
||||
videoUrl = firstVideo.baseUrl!;
|
||||
|
||||
/// 根据currentAudioQa 重新设置audioUrl
|
||||
@ -152,6 +164,12 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
|
||||
Future playerInit({video, audio, seekToTime, duration}) async {
|
||||
/// 设置/恢复 屏幕亮度
|
||||
if (brightness != null) {
|
||||
ScreenBrightness().setScreenBrightness(brightness!);
|
||||
} else {
|
||||
ScreenBrightness().resetScreenBrightness();
|
||||
}
|
||||
await plPlayerController.setDataSource(
|
||||
DataSource(
|
||||
videoSource: video ?? videoUrl,
|
||||
@ -234,17 +252,25 @@ class VideoDetailController extends GetxController
|
||||
defaultValue: VideoDecodeFormats.values.last.code))!;
|
||||
try {
|
||||
// 当前视频没有对应格式返回第一个
|
||||
currentDecodeFormats =
|
||||
supportDecodeFormats.contains(supportDecodeFormats)
|
||||
? supportDecodeFormats
|
||||
: supportDecodeFormats.first;
|
||||
} catch (_) {}
|
||||
currentDecodeFormats = supportDecodeFormats
|
||||
.contains(currentDecodeFormats)
|
||||
? currentDecodeFormats
|
||||
: VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
/// 取出符合当前解码格式的videoItem
|
||||
firstVideo = videosList
|
||||
.firstWhere((e) => e.codecs!.startsWith(currentDecodeFormats.code));
|
||||
try {
|
||||
firstVideo = videosList.firstWhere(
|
||||
(e) => e.codecs!.startsWith(currentDecodeFormats.code));
|
||||
} catch (_) {
|
||||
firstVideo = videosList.first;
|
||||
}
|
||||
videoUrl = firstVideo.baseUrl!;
|
||||
} catch (_) {}
|
||||
} catch (err) {
|
||||
print(err);
|
||||
}
|
||||
|
||||
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
||||
late AudioItem firstAudio;
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
@ -51,6 +53,12 @@ class VideoIntroController extends GetxController {
|
||||
RxInt lastPlayCid = 0.obs;
|
||||
var userInfo;
|
||||
|
||||
// 同时观看
|
||||
bool isShowOnlineTotal = false;
|
||||
RxInt totel = 1.obs;
|
||||
Timer? timer;
|
||||
bool isPaused = false;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@ -78,6 +86,12 @@ class VideoIntroController extends GetxController {
|
||||
}
|
||||
userLogin = userInfo != null;
|
||||
lastPlayCid.value = int.parse(Get.parameters['cid']!);
|
||||
isShowOnlineTotal =
|
||||
setting.get(SettingBoxKey.enableOnlineTotal, defaultValue: false);
|
||||
if (isShowOnlineTotal) {
|
||||
queryOnlineTotal();
|
||||
startTimer(); // 在页面加载时启动定时器
|
||||
}
|
||||
}
|
||||
|
||||
// 获取视频简介&分p
|
||||
@ -417,4 +431,33 @@ class VideoIntroController extends GetxController {
|
||||
lastPlayCid.value = cid;
|
||||
await queryVideoIntro();
|
||||
}
|
||||
|
||||
void startTimer() {
|
||||
const duration = Duration(seconds: 10); // 设置定时器间隔为10秒
|
||||
timer = Timer.periodic(duration, (Timer timer) {
|
||||
if (!isPaused) {
|
||||
queryOnlineTotal(); // 定时器回调函数,发起请求
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 查看同时在看人数
|
||||
Future queryOnlineTotal() async {
|
||||
var result = await VideoHttp.onlineTotal(
|
||||
aid: IdUtils.bv2av(bvid),
|
||||
bvid: bvid,
|
||||
cid: lastPlayCid.value,
|
||||
);
|
||||
if (result['status']) {
|
||||
totel.value = int.parse(result['data']['total']);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
if (timer != null) {
|
||||
timer!.cancel(); // 销毁页面时取消定时器
|
||||
}
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
late final dynamic owner;
|
||||
late final dynamic follower;
|
||||
late final dynamic followStatus;
|
||||
late int mid;
|
||||
late String memberHeroTag;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -160,14 +162,15 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
// 用户主页
|
||||
onPushMember() {
|
||||
feedBack();
|
||||
int mid = !loadingStatus
|
||||
mid = !loadingStatus
|
||||
? widget.videoDetail!.owner!.mid
|
||||
: videoItem['owner'].mid;
|
||||
memberHeroTag = Utils.makeHeroTag(mid);
|
||||
String face = !loadingStatus
|
||||
? widget.videoDetail!.owner!.face
|
||||
: videoItem['owner'].face;
|
||||
Get.toNamed('/member?mid=$mid',
|
||||
arguments: {'face': face, 'heroTag': (mid + 99).toString()});
|
||||
arguments: {'face': face, 'heroTag': memberHeroTag});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -255,6 +258,17 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
color: t.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
if (videoIntroController.isShowOnlineTotal)
|
||||
Obx(
|
||||
() => Text(
|
||||
'${videoIntroController.totel.value}人在看',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: t.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -28,7 +28,25 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
episodes = widget.ugcSeason.sections!.first.episodes!;
|
||||
|
||||
/// 根据 cid 找到对应集,找到对应 episodes
|
||||
/// 有多个episodes时,只显示其中一个
|
||||
/// TODO 同时显示多个合集
|
||||
List<SectionItem> sections = widget.ugcSeason.sections!;
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||
for (int j = 0; j < episodesList.length; j++) {
|
||||
if (episodesList[j].cid == widget.cid) {
|
||||
episodes = episodesList;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 取对应 season_id 的 episodes
|
||||
// episodes = widget.ugcSeason.sections!
|
||||
// .firstWhere((e) => e.seasonId == widget.ugcSeason.id)
|
||||
// .episodes!;
|
||||
currentIndex = episodes.indexWhere((e) => e.cid == widget.cid);
|
||||
}
|
||||
|
||||
@ -136,7 +154,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'${currentIndex + 1}/${widget.ugcSeason.epCount}',
|
||||
'${currentIndex + 1}/${episodes.length}',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/http/reply.dart';
|
||||
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class VideoReplyController extends GetxController {
|
||||
VideoReplyController(
|
||||
@ -25,13 +28,26 @@ class VideoReplyController extends GetxController {
|
||||
bool isLoadingMore = false;
|
||||
RxString noMore = ''.obs;
|
||||
int ps = 20;
|
||||
RxInt count = 0.obs;
|
||||
// 当前回复的回复
|
||||
ReplyItemModel? currentReplyItem;
|
||||
|
||||
ReplySortType sortType = ReplySortType.time;
|
||||
ReplySortType _sortType = ReplySortType.time;
|
||||
RxString sortTypeTitle = ReplySortType.time.titles.obs;
|
||||
RxString sortTypeLabel = ReplySortType.time.labels.obs;
|
||||
|
||||
Box setting = GStrorage.setting;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
int deaultReplySortIndex =
|
||||
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
|
||||
_sortType = ReplySortType.values[deaultReplySortIndex];
|
||||
sortTypeTitle.value = _sortType.titles;
|
||||
sortTypeLabel.value = _sortType.labels;
|
||||
}
|
||||
|
||||
Future queryReplyList({type = 'init'}) async {
|
||||
isLoadingMore = true;
|
||||
if (type == 'init') {
|
||||
@ -45,7 +61,7 @@ class VideoReplyController extends GetxController {
|
||||
pageNum: currentPage + 1,
|
||||
ps: ps,
|
||||
type: ReplyType.video.index,
|
||||
sort: sortType.index,
|
||||
sort: _sortType.index,
|
||||
);
|
||||
if (res['status']) {
|
||||
List<ReplyItemModel> replies = res['data'].replies;
|
||||
@ -81,6 +97,7 @@ class VideoReplyController extends GetxController {
|
||||
replyList.addAll(replies);
|
||||
}
|
||||
}
|
||||
count.value = res['data'].page.count;
|
||||
isLoadingMore = false;
|
||||
return res;
|
||||
}
|
||||
@ -92,23 +109,26 @@ class VideoReplyController extends GetxController {
|
||||
|
||||
// 排序搜索评论
|
||||
queryBySort() {
|
||||
feedBack();
|
||||
switch (sortType) {
|
||||
case ReplySortType.time:
|
||||
sortType = ReplySortType.like;
|
||||
break;
|
||||
case ReplySortType.like:
|
||||
sortType = ReplySortType.reply;
|
||||
break;
|
||||
case ReplySortType.reply:
|
||||
sortType = ReplySortType.time;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
sortTypeTitle.value = sortType.titles;
|
||||
sortTypeLabel.value = sortType.labels;
|
||||
currentPage = 0;
|
||||
replyList.clear();
|
||||
queryReplyList(type: 'init');
|
||||
EasyThrottle.throttle('queryBySort', const Duration(seconds: 1), () {
|
||||
feedBack();
|
||||
switch (_sortType) {
|
||||
case ReplySortType.time:
|
||||
_sortType = ReplySortType.like;
|
||||
break;
|
||||
case ReplySortType.like:
|
||||
_sortType = ReplySortType.reply;
|
||||
break;
|
||||
case ReplySortType.reply:
|
||||
_sortType = ReplySortType.time;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
sortTypeTitle.value = _sortType.titles;
|
||||
sortTypeLabel.value = _sortType.labels;
|
||||
currentPage = 0;
|
||||
noMore.value = '';
|
||||
replyList.clear();
|
||||
queryReplyList(type: 'init');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -160,9 +160,9 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
scale: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
_videoReplyController.sortTypeTitle.value,
|
||||
key: ValueKey<String>(
|
||||
_videoReplyController.sortTypeTitle.value),
|
||||
'共${_videoReplyController.count.value}条回复',
|
||||
key: ValueKey<int>(
|
||||
_videoReplyController.count.value),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -591,7 +591,8 @@ InlineSpan buildContent(
|
||||
if (content.jumpUrl.isNotEmpty && hasMatchMember) {
|
||||
List urlKeys = content.jumpUrl.keys.toList();
|
||||
matchUrl = matchMember.splitMapJoin(
|
||||
RegExp("(?:${urlKeys.join("|")})"),
|
||||
/// RegExp.escape() 转义特殊字符
|
||||
RegExp(RegExp.escape(urlKeys.join("|"))),
|
||||
onMatch: (Match match) {
|
||||
String matchStr = match[0]!;
|
||||
spanChilds.add(
|
||||
|
@ -37,12 +37,15 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
PlPlayerController? plPlayerController;
|
||||
final ScrollController _extendNestCtr = ScrollController();
|
||||
late StreamController<double> appbarStream;
|
||||
final VideoIntroController videoIntroController =
|
||||
Get.put(VideoIntroController(), tag: Get.arguments['heroTag']);
|
||||
|
||||
PlayerStatus playerStatus = PlayerStatus.playing;
|
||||
// bool isShowCover = true;
|
||||
double doubleOffset = 0;
|
||||
|
||||
Box localCache = GStrorage.localCache;
|
||||
Box setting = GStrorage.setting;
|
||||
late double statusBarHeight;
|
||||
final videoHeight = Get.size.width * 9 / 16;
|
||||
late Future _futureBuilderFuture;
|
||||
@ -95,7 +98,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
plPlayerController!.pause();
|
||||
plPlayerController!.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@ -103,7 +105,12 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
@override
|
||||
// 离开当前页面时
|
||||
void didPushNext() async {
|
||||
/// 开启
|
||||
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)) {
|
||||
videoDetailController.brightness = plPlayerController!.brightness.value;
|
||||
}
|
||||
videoDetailController.defaultST = plPlayerController!.position.value;
|
||||
videoIntroController.isPaused = true;
|
||||
plPlayerController!.pause();
|
||||
super.didPushNext();
|
||||
}
|
||||
@ -112,6 +119,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
// 返回当前页面时
|
||||
void didPopNext() async {
|
||||
videoDetailController.playerInit();
|
||||
videoIntroController.isPaused = false;
|
||||
if (_extendNestCtr.position.pixels == 0) {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
plPlayerController!.play();
|
||||
|
@ -387,9 +387,12 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
// 当前选中的解码格式
|
||||
VideoDecodeFormats currentDecodeFormats =
|
||||
widget.videoDetailCtr!.currentDecodeFormats;
|
||||
VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
||||
// 当前视频可用的解码格式
|
||||
List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
List list = videoFormat.first.codecs!;
|
||||
List list = videoFormat
|
||||
.firstWhere((e) => e.quality == firstVideo.quality!.code)
|
||||
.codecs!;
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
@ -483,10 +486,12 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
fuc: () {
|
||||
fuc: () async {
|
||||
// 销毁播放器实例
|
||||
widget.controller!.dispose(type: 'all');
|
||||
Get.offAll(const MainApp());
|
||||
await widget.controller!.dispose(type: 'all');
|
||||
if (mounted) {
|
||||
Navigator.popUntil(context, (route) => route.isFirst);
|
||||
}
|
||||
},
|
||||
),
|
||||
const Spacer(),
|
||||
@ -506,9 +511,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
_.togglePlaybackSpeed();
|
||||
},
|
||||
onPressed: () => showSetSpeedSheet(),
|
||||
child: Text(
|
||||
'${_.playbackSpeed.toString()}X',
|
||||
style: textStyle,
|
||||
|
@ -619,7 +619,7 @@ class PlPlayerController {
|
||||
try {
|
||||
brightness.value = brightnes;
|
||||
ScreenBrightness().setScreenBrightness(brightnes);
|
||||
setVideoBrightness();
|
||||
// setVideoBrightness();
|
||||
} catch (e) {
|
||||
throw 'Failed to set brightness';
|
||||
}
|
||||
@ -662,27 +662,24 @@ class PlPlayerController {
|
||||
}
|
||||
|
||||
/// 缓存fit
|
||||
Future<void> setVideoFit() async {
|
||||
videoStorage.put(VideoBoxKey.videoBrightness, _videoFit.value.name);
|
||||
}
|
||||
// Future<void> setVideoFit() async {
|
||||
// videoStorage.put(VideoBoxKey.videoBrightness, _videoFit.value.name);
|
||||
// }
|
||||
|
||||
/// 读取fit
|
||||
Future<void> getVideoFit() async {
|
||||
String fitValue =
|
||||
videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 'contain');
|
||||
_videoFit.value = videoFitType
|
||||
.firstWhere((element) => element['attr'] == fitValue)['attr'];
|
||||
}
|
||||
|
||||
/// 缓存亮度
|
||||
Future<void> setVideoBrightness() async {}
|
||||
// Future<void> getVideoFit() async {
|
||||
// String fitValue =
|
||||
// videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 'contain');
|
||||
// _videoFit.value = videoFitType
|
||||
// .firstWhere((element) => element['attr'] == fitValue)['attr'];
|
||||
// }
|
||||
|
||||
/// 读取亮度
|
||||
Future<void> getVideoBrightness() async {
|
||||
double brightnessValue =
|
||||
videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 0.5);
|
||||
setBrightness(brightnessValue);
|
||||
}
|
||||
// Future<void> getVideoBrightness() async {
|
||||
// double brightnessValue =
|
||||
// videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 0.5);
|
||||
// setBrightness(brightnessValue);
|
||||
// }
|
||||
|
||||
set controls(bool visible) {
|
||||
_showControls.value = visible;
|
||||
@ -760,36 +757,41 @@ class PlPlayerController {
|
||||
|
||||
Future<void> dispose({String type = 'single'}) async {
|
||||
// 每次减1,最后销毁
|
||||
if (type == 'single') {
|
||||
if (type == 'single' && playerCount.value > 1) {
|
||||
_playerCount.value -= 1;
|
||||
_heartDuration = 0;
|
||||
if (playerCount.value > 0) {
|
||||
return;
|
||||
}
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
_playerCount.value = 0;
|
||||
try {
|
||||
_timer?.cancel();
|
||||
_timerForVolume?.cancel();
|
||||
_timerForGettingVolume?.cancel();
|
||||
timerForTrackingMouse?.cancel();
|
||||
_timerForSeek?.cancel();
|
||||
videoFitChangedTimer?.cancel();
|
||||
// _position.close();
|
||||
_playerEventSubs?.cancel();
|
||||
// _sliderPosition.close();
|
||||
// _sliderTempPosition.close();
|
||||
// _isSliderMoving.close();
|
||||
// _duration.close();
|
||||
// _buffered.close();
|
||||
// _showControls.close();
|
||||
// _controlsLock.close();
|
||||
|
||||
_timer?.cancel();
|
||||
_timerForVolume?.cancel();
|
||||
_timerForGettingVolume?.cancel();
|
||||
timerForTrackingMouse?.cancel();
|
||||
_timerForSeek?.cancel();
|
||||
videoFitChangedTimer?.cancel();
|
||||
_position.close();
|
||||
_playerEventSubs?.cancel();
|
||||
_sliderPosition.close();
|
||||
_sliderTempPosition.close();
|
||||
_isSliderMoving.close();
|
||||
_duration.close();
|
||||
_buffered.close();
|
||||
_showControls.close();
|
||||
_controlsLock.close();
|
||||
// playerStatus.status.close();
|
||||
// dataStatus.status.close();
|
||||
|
||||
playerStatus.status.close();
|
||||
dataStatus.status.close();
|
||||
|
||||
removeListeners();
|
||||
await _videoPlayerController?.dispose();
|
||||
_videoPlayerController = null;
|
||||
_instance = null;
|
||||
removeListeners();
|
||||
await _videoPlayerController?.dispose();
|
||||
_videoPlayerController = null;
|
||||
_instance = null;
|
||||
// 关闭所有视频页面恢复亮度
|
||||
resetBrightness();
|
||||
} catch (err) {
|
||||
print(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
});
|
||||
}
|
||||
});
|
||||
widget.controller.brightness.value = value;
|
||||
}
|
||||
|
||||
Future<void> triggerFullScreen() async {
|
||||
|
@ -1,4 +1,8 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/pages/about/index.dart';
|
||||
import 'package:pilipala/pages/blacklist/index.dart';
|
||||
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
||||
@ -18,6 +22,7 @@ import 'package:pilipala/pages/search/index.dart';
|
||||
import 'package:pilipala/pages/searchResult/index.dart';
|
||||
import 'package:pilipala/pages/setting/extra_setting.dart';
|
||||
import 'package:pilipala/pages/setting/pages/color_select.dart';
|
||||
import 'package:pilipala/pages/setting/pages/display_mode.dart';
|
||||
import 'package:pilipala/pages/setting/pages/font_size_select.dart';
|
||||
import 'package:pilipala/pages/setting/play_setting.dart';
|
||||
import 'package:pilipala/pages/setting/privacy_setting.dart';
|
||||
@ -27,15 +32,20 @@ import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
||||
import 'package:pilipala/pages/webview/index.dart';
|
||||
import 'package:pilipala/pages/setting/index.dart';
|
||||
import 'package:pilipala/pages/media/index.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
Box setting = GStrorage.setting;
|
||||
bool iosTransition =
|
||||
setting.get(SettingBoxKey.iosTransition, defaultValue: false);
|
||||
|
||||
class Routes {
|
||||
static final List<GetPage> getPages = [
|
||||
// 首页(推荐)
|
||||
GetPage(name: '/', page: () => const HomePage()),
|
||||
CustomGetPage(name: '/', page: () => const HomePage()),
|
||||
// 热门
|
||||
GetPage(name: '/hot', page: () => const HotPage()),
|
||||
CustomGetPage(name: '/hot', page: () => const HotPage()),
|
||||
// 视频详情
|
||||
GetPage(name: '/video', page: () => const VideoDetailPage()),
|
||||
CustomGetPage(name: '/video', page: () => const VideoDetailPage()),
|
||||
// 图片预览
|
||||
GetPage(
|
||||
name: '/preview',
|
||||
@ -45,51 +55,77 @@ class Routes {
|
||||
showCupertinoParallax: false,
|
||||
),
|
||||
//
|
||||
GetPage(name: '/webview', page: () => const WebviewPage()),
|
||||
CustomGetPage(name: '/webview', page: () => const WebviewPage()),
|
||||
// 设置
|
||||
GetPage(name: '/setting', page: () => const SettingPage()),
|
||||
CustomGetPage(name: '/setting', page: () => const SettingPage()),
|
||||
//
|
||||
GetPage(name: '/media', page: () => const MediaPage()),
|
||||
CustomGetPage(name: '/media', page: () => const MediaPage()),
|
||||
//
|
||||
GetPage(name: '/fav', page: () => const FavPage()),
|
||||
CustomGetPage(name: '/fav', page: () => const FavPage()),
|
||||
//
|
||||
GetPage(name: '/favDetail', page: () => const FavDetailPage()),
|
||||
CustomGetPage(name: '/favDetail', page: () => const FavDetailPage()),
|
||||
// 稍后再看
|
||||
GetPage(name: '/later', page: () => const LaterPage()),
|
||||
CustomGetPage(name: '/later', page: () => const LaterPage()),
|
||||
// 历史记录
|
||||
GetPage(name: '/history', page: () => const HistoryPage()),
|
||||
CustomGetPage(name: '/history', page: () => const HistoryPage()),
|
||||
// 搜索页面
|
||||
GetPage(name: '/search', page: () => const SearchPage()),
|
||||
CustomGetPage(name: '/search', page: () => const SearchPage()),
|
||||
// 搜索结果
|
||||
GetPage(name: '/searchResult', page: () => const SearchResultPage()),
|
||||
CustomGetPage(name: '/searchResult', page: () => const SearchResultPage()),
|
||||
// 动态
|
||||
GetPage(name: '/dynamics', page: () => const DynamicsPage()),
|
||||
CustomGetPage(name: '/dynamics', page: () => const DynamicsPage()),
|
||||
// 动态详情
|
||||
GetPage(name: '/dynamicDetail', page: () => const DynamicDetailPage()),
|
||||
CustomGetPage(
|
||||
name: '/dynamicDetail', page: () => const DynamicDetailPage()),
|
||||
// 关注
|
||||
GetPage(name: '/follow', page: () => const FollowPage()),
|
||||
CustomGetPage(name: '/follow', page: () => const FollowPage()),
|
||||
// 粉丝
|
||||
GetPage(name: '/fan', page: () => const FansPage()),
|
||||
CustomGetPage(name: '/fan', page: () => const FansPage()),
|
||||
// 直播详情
|
||||
GetPage(name: '/liveRoom', page: () => const LiveRoomPage()),
|
||||
CustomGetPage(name: '/liveRoom', page: () => const LiveRoomPage()),
|
||||
// 用户中心
|
||||
GetPage(name: '/member', page: () => const MemberPage()),
|
||||
CustomGetPage(name: '/member', page: () => const MemberPage()),
|
||||
// 二级回复
|
||||
GetPage(name: '/replyReply', page: () => const VideoReplyReplyPanel()),
|
||||
CustomGetPage(
|
||||
name: '/replyReply', page: () => const VideoReplyReplyPanel()),
|
||||
|
||||
// 播放设置
|
||||
GetPage(name: '/playSetting', page: () => const PlaySetting()),
|
||||
CustomGetPage(name: '/playSetting', page: () => const PlaySetting()),
|
||||
// 外观设置
|
||||
GetPage(name: '/styleSetting', page: () => const StyleSetting()),
|
||||
CustomGetPage(name: '/styleSetting', page: () => const StyleSetting()),
|
||||
// 隐私设置
|
||||
GetPage(name: '/privacySetting', page: () => const PrivacySetting()),
|
||||
CustomGetPage(name: '/privacySetting', page: () => const PrivacySetting()),
|
||||
// 其他设置
|
||||
GetPage(name: '/extraSetting', page: () => const ExtraSetting()),
|
||||
CustomGetPage(name: '/extraSetting', page: () => const ExtraSetting()),
|
||||
//
|
||||
GetPage(name: '/blackListPage', page: () => const BlackListPage()),
|
||||
GetPage(name: '/colorSetting', page: () => const ColorSelectPage()),
|
||||
GetPage(name: '/fontSizeSetting', page: () => const FontSizeSelectPage()),
|
||||
CustomGetPage(name: '/blackListPage', page: () => const BlackListPage()),
|
||||
CustomGetPage(name: '/colorSetting', page: () => const ColorSelectPage()),
|
||||
CustomGetPage(
|
||||
name: '/fontSizeSetting', page: () => const FontSizeSelectPage()),
|
||||
// 屏幕帧率
|
||||
CustomGetPage(
|
||||
name: '/displayModeSetting', page: () => const SetDiaplayMode()),
|
||||
// 关于
|
||||
GetPage(name: '/about', page: () => const AboutPage()),
|
||||
CustomGetPage(name: '/about', page: () => const AboutPage()),
|
||||
];
|
||||
}
|
||||
|
||||
class CustomGetPage extends GetPage {
|
||||
bool? fullscreen = false;
|
||||
|
||||
CustomGetPage({
|
||||
name,
|
||||
page,
|
||||
this.fullscreen,
|
||||
transitionDuration,
|
||||
}) : super(
|
||||
name: name,
|
||||
page: page,
|
||||
curve: Curves.linear,
|
||||
transition: iosTransition ? Transition.cupertino : Transition.native,
|
||||
showCupertinoParallax: false,
|
||||
popGesture: false,
|
||||
transitionDuration: transitionDuration,
|
||||
fullscreenDialog: fullscreen != null && fullscreen,
|
||||
);
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ class GStrorage {
|
||||
}
|
||||
|
||||
class SettingBoxKey {
|
||||
/// 播放器
|
||||
static const String btmProgressBehavior = 'btmProgressBehavior';
|
||||
static const String defaultVideoSpeed = 'defaultVideoSpeed';
|
||||
static const String autoUpgradeEnable = 'autoUpgradeEnable';
|
||||
@ -94,15 +95,23 @@ class SettingBoxKey {
|
||||
static const String danmakuEnable = 'danmakuEnable';
|
||||
static const String defaultPicQa = 'defaultPicQa';
|
||||
static const String enableHA = 'enableHA';
|
||||
static const String enableOnlineTotal = 'enableOnlineTotal';
|
||||
static const String enableAutoBrightness = 'enableAutoBrightness';
|
||||
|
||||
/// 隐私
|
||||
static const String blackMidsList = 'blackMidsList';
|
||||
|
||||
/// 其他
|
||||
static const String autoUpdate = 'autoUpdate';
|
||||
static const String replySortType = 'replySortType';
|
||||
static const String defaultDynamicType = 'defaultDynamicType';
|
||||
|
||||
/// 外观
|
||||
static const String themeMode = 'themeMode';
|
||||
static const String defaultTextScale = 'textScale';
|
||||
static const String dynamicColor = 'dynamicColor'; // bool
|
||||
static const String customColor = 'customColor'; // 自定义主题色
|
||||
static const String iosTransition = 'iosTransition'; // ios路由
|
||||
}
|
||||
|
||||
class LocalCacheKey {
|
||||
|
@ -4,6 +4,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get_utils/get_utils.dart';
|
||||
@ -210,6 +211,7 @@ class Utils {
|
||||
bool isUpdate = Utils.needUpdate(currentInfo.version, data.tagName!);
|
||||
if (isUpdate) {
|
||||
SmartDialog.show(
|
||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('🎉 发现新版本 '),
|
||||
@ -228,22 +230,27 @@ class Utils {
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => SmartDialog.dismiss(),
|
||||
child: Text(
|
||||
'稍后',
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
)),
|
||||
onPressed: () => SmartDialog.dismiss(),
|
||||
child: Text(
|
||||
'稍后',
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await SmartDialog.dismiss();
|
||||
launchUrl(
|
||||
Uri.parse(
|
||||
'https://github.com/guozhigq/pilipala/releases'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: const Text('去下载')),
|
||||
onPressed: () async {
|
||||
await SmartDialog.dismiss();
|
||||
launchUrl(
|
||||
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: const Text('网盘下载'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => matchVersion(data),
|
||||
child: const Text('Github下载'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -251,4 +258,27 @@ class Utils {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 下载适用于当前系统的安装包
|
||||
static Future matchVersion(data) async {
|
||||
await SmartDialog.dismiss();
|
||||
// 获取设备信息
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
if (Platform.isAndroid) {
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
// [arm64-v8a]
|
||||
String abi = androidInfo.supportedAbis.first;
|
||||
late String downloadUrl;
|
||||
for (var i in data.assets) {
|
||||
if (i.downloadUrl.contains(abi)) {
|
||||
downloadUrl = i.downloadUrl;
|
||||
}
|
||||
}
|
||||
// 应用外下载
|
||||
launchUrl(
|
||||
Uri.parse(downloadUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -438,6 +438,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
flutter_displaymode:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_displaymode
|
||||
sha256: "42c5e9abd13d28ed74f701b60529d7f8416947e58256e6659c5550db719c57ef"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.4
|
||||
version: 1.0.5
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.6 <3.0.0"
|
||||
@ -115,6 +115,8 @@ dependencies:
|
||||
flutter_svg: ^2.0.7
|
||||
# 防抖节流
|
||||
easy_debounce: ^2.0.3
|
||||
# 高帧率
|
||||
flutter_displaymode: ^0.6.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user