Compare commits

..

24 Commits

Author SHA1 Message Date
599d6983fc v1.0.2 更新 2023-08-19 21:33:39 +08:00
b7eed8578a mod: 请求节流 2023-08-19 20:37:42 +08:00
ec9d9739fe mod: 异常状态样式 2023-08-19 17:50:26 +08:00
740116e873 feat: 检查更新 2023-08-19 16:33:39 +08:00
2b0dc9d285 fix: 登录/退出登录页面刷新 2023-08-19 15:25:00 +08:00
7c2518bcd2 mod: 首页推荐picture类型 2023-08-19 11:34:36 +08:00
9db76e99db fix: 登录成功跳转 mod: 退出登录提示 2023-08-19 11:33:39 +08:00
40849cb68d opt: 全屏方式优化 2023-08-19 08:35:17 +08:00
b435023c99 feat: 直播、推荐、热门封面图片保存 2023-08-18 20:16:15 +08:00
b55568ef2a mod: 首页推荐代码 2023-08-18 18:22:20 +08:00
47a3c964c0 fix: 番剧记忆播放 2023-08-18 12:11:26 +08:00
85ff7c7a92 feat: 动态跳转番剧 2023-08-18 11:12:18 +08:00
083d05ed7e feat: 观看记录增加稍后再看功能 2023-08-18 10:24:09 +08:00
08862e7f72 feat: 稍后再看页面完善 2023-08-18 10:11:50 +08:00
04d953a3a2 fix: dio cookie读写 2023-08-18 09:30:31 +08:00
be1cc25d12 mod: 登录后状态 2023-08-17 22:44:01 +08:00
adff2f2828 fix: mac音量控制 2023-08-17 20:14:31 +08:00
592b32fc7a fix: 分p切换cid未更改 2023-08-17 16:00:37 +08:00
d9a758464c Merge branch 'main' of github.com:guozhigq/pilipala 2023-08-17 14:32:38 +08:00
0c067f7ca6 feat: 视频视图比例调整 2023-08-17 14:32:22 +08:00
b6aa144205 Update issue templates 2023-08-17 12:21:57 +08:00
438a94e298 Update main.yml
取消重命名拼接日期
2023-08-17 10:51:45 +08:00
b800f1b83b 更新日志 2023-08-17 10:35:55 +08:00
ddceda1184 mod: 视频播放器依赖升级 2023-08-17 10:12:01 +08:00
95 changed files with 1405 additions and 635 deletions

23
.github/ISSUE_TEMPLATE/bug-反馈.md vendored Normal file
View File

@ -0,0 +1,23 @@
---
name: Bug 反馈
about: 描述你所遇到的bug
title: ''
labels: 问题反馈
assignees: guozhigq
---
### 问题描述
请提供一个清晰而简明的问题描述。
### 复现步骤
请提供复现该问题所需的具体步骤。
### 预期行为
请描述你期望的正确行为或结果。
### 系统信息
请提供关于您的环境的详细信息,包括操作系统、浏览器版本等。
### 相关截图或日志
如果有的话,请提供相关的截图、错误日志或其他有助于解决问题的信息。

20
.github/ISSUE_TEMPLATE/功能请求.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: 功能请求
about: 对于功能的一些建议
title: ''
labels: 功能
assignees: guozhigq
---
### 功能描述
请提供对所请求功能的清晰描述。
### 目标
请描述你希望通过这个功能实现的目标。
### 解决方案
如果你有任何关于如何实现这个功能的想法或建议,请在这里提供。
### 其他
请提供已实现该功能或类似功能的应用

View File

@ -59,16 +59,16 @@ jobs:
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT
- name: 获取当前日期
id: date
run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT
# - name: 获取当前日期
# id: date
# run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT
- name: 重命名应用 Pili-arm64-v8a-*.*.*.0101.apk
run: |
DATE=${{ steps.date.outputs.date }}
# DATE=${{ steps.date.outputs.date }}
for file in build/app/outputs/flutter-apk/app-*-release.apk; do
if [[ $file =~ app-(.*)-release.apk ]]; then
new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}-${{ steps.version.outputs.version }}($DATE).apk"
new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}-${{ steps.version.outputs.version }}.apk"
mv "$file" "$new_file_name"
fi
done

1
assets/images/error.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

7
change_log/1.0.1.0817.md Normal file
View File

@ -0,0 +1,7 @@
## 1.0.1
### 修复
+ 升级播放器依赖
+ android平台 AV1格式视频支持
+ 视频全屏功能

19
change_log/1.0.2.0819.md Normal file
View File

@ -0,0 +1,19 @@
## 1.0.2
### 新功能
+ 自动检查更新
+ 封面图片保存
+ 动态跳转番剧
+ 历史记录番剧记忆播放
+ 一键清空稍后再看
### 修复
+ 切换分P cid未切换
+ cookie存储问题
+ 登录/退出登录问题
### 优化
+ 页面空/异常状态样式
+ 退出登录提示
+ 请求节流
+ 全屏播放

View File

@ -5,6 +5,8 @@ PODS:
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_volume_controller (0.0.1):
- Flutter
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
@ -46,6 +48,7 @@ DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`)
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
@ -74,6 +77,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
:path: Flutter
flutter_volume_controller:
:path: ".symlinks/plugins/flutter_volume_controller/ios"
image_gallery_saver:
:path: ".symlinks/plugins/image_gallery_saver/ios"
media_kit_libs_ios_video:
@ -109,6 +114,7 @@ SPEC CHECKSUMS:
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
class AnimatedDialog extends StatefulWidget {
const AnimatedDialog({Key? key, required this.child}) : super(key: key);
const AnimatedDialog({Key? key, required this.child, this.closeFn})
: super(key: key);
final Widget child;
final Function? closeFn;
@override
State<StatefulWidget> createState() => AnimatedDialogState();
@ -39,12 +41,16 @@ class AnimatedDialogState extends State<AnimatedDialog>
Widget build(BuildContext context) {
return Material(
color: Colors.black.withOpacity(opacityAnimation!.value),
child: Center(
child: FadeTransition(
opacity: scaleAnimation!,
child: ScaleTransition(
scale: scaleAnimation!,
child: widget.child,
child: InkWell(
splashColor: Colors.transparent,
onTap: () => widget.closeFn!(),
child: Center(
child: FadeTransition(
opacity: scaleAnimation!,
child: ScaleTransition(
scale: scaleAnimation!,
child: widget.child,
),
),
),
),

View File

@ -1,31 +1,41 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class HttpError extends StatelessWidget {
const HttpError({required this.errMsg, required this.fn, super.key});
const HttpError(
{required this.errMsg, required this.fn, this.btnText, super.key});
final String? errMsg;
final Function()? fn;
final String? btnText;
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: SizedBox(
height: 150,
height: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
"assets/images/error.svg",
height: 200,
),
const SizedBox(height: 20),
Text(
errMsg ?? '请求异常',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
fn!();
},
child: const Text('点击重试'))
const SizedBox(height: 30),
OutlinedButton.icon(
onPressed: () {
fn!();
},
icon: const Icon(Icons.arrow_forward_outlined, size: 20),
label: Text(btnText ?? '点击重试'),
)
],
),
),

View File

@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/pages/rcmd/controller.dart';
import 'package:pilipala/utils/utils.dart';
class LiveCard extends StatelessWidget {
@ -95,7 +93,7 @@ class LiveContent extends StatelessWidget {
liveItem.title,
textAlign: TextAlign.start,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
maxLines: Get.find<RcmdController>().crossAxisCount,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class NoData extends StatelessWidget {
const NoData({super.key});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: SizedBox(
height: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
"assets/images/error.svg",
height: 200,
),
const SizedBox(height: 20),
Text(
'没有数据',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleSmall,
),
],
),
),
);
}
}

View File

@ -1,42 +1,82 @@
import 'package:flutter/material.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/download.dart';
class OverlayPop extends StatelessWidget {
final dynamic videoItem;
const OverlayPop({super.key, this.videoItem});
final Function? closeFn;
const OverlayPop({super.key, this.videoItem, this.closeFn});
@override
Widget build(BuildContext context) {
double imgWidth = MediaQuery.of(context).size.width - 8 * 2;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
margin: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(6.0),
borderRadius: BorderRadius.circular(10.0),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(6.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
NetworkImgLayer(
width: (MediaQuery.of(context).size.width - 16),
height: (MediaQuery.of(context).size.width - 16) /
StyleString.aspectRatio,
src: videoItem.pic!,
),
Padding(
padding: const EdgeInsets.fromLTRB(12, 15, 10, 15),
child: Text(
videoItem.title!,
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
NetworkImgLayer(
width: imgWidth,
height: imgWidth / StyleString.aspectRatio,
src: videoItem.pic!,
quality: 100,
),
),
],
),
Positioned(
right: 8,
top: 8,
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius:
const BorderRadius.all(Radius.circular(20))),
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () => closeFn!(),
icon: const Icon(
Icons.close,
size: 18,
color: Colors.white,
),
),
),
),
],
),
Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 8, 10),
child: Row(
children: [
Expanded(
child: Text(
videoItem.title!,
),
),
const SizedBox(width: 4),
IconButton(
tooltip: '保存封面图',
onPressed: () async {
await DownloadUtils.downloadImg(
videoItem.pic ?? videoItem.cover);
// closeFn!();
},
icon: const Icon(Icons.download, size: 20),
)
],
)),
],
),
);
}

View File

@ -37,11 +37,11 @@ class VideoCardH extends StatelessWidget {
longPress!();
}
},
onLongPressEnd: (details) {
if (longPressEnd != null) {
longPressEnd!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
try {
@ -55,7 +55,7 @@ class VideoCardH extends StatelessWidget {
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.safeSpace, 7, StyleString.safeSpace, 7),
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (context, boxConstraints) {
double width =

View File

@ -14,8 +14,7 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
// 视频卡片 - 垂直布局
class VideoCardV extends StatelessWidget {
// ignore: prefer_typing_uninitialized_variables
final videoItem;
final dynamic videoItem;
final Function()? longPress;
final Function()? longPressEnd;
@ -61,6 +60,16 @@ class VideoCardV extends StatelessWidget {
'heroTag': heroTag,
});
break;
default:
SmartDialog.showToast(videoItem.goto);
Get.toNamed(
'/webview',
parameters: {
'url': videoItem.uri,
'type': 'url',
'pageTitle': videoItem.title,
},
);
}
}
@ -80,57 +89,29 @@ class VideoCardV extends StatelessWidget {
longPress!();
}
},
onLongPressEnd: (details) {
if (longPressEnd != null) {
longPressEnd!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async => onPushDetail(heroTag),
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: StyleString.imgRadius,
topRight: StyleString.imgRadius,
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
child: AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
),
// if (videoItem.stat.view is int &&
// videoItem.stat.danmaku is int)
// Positioned(
// left: 0,
// right: 0,
// bottom: 0,
// child: AnimatedOpacity(
// opacity: 1,
// duration: const Duration(milliseconds: 200),
// child: VideoStat(
// view: videoItem.stat.view,
// danmaku: videoItem.stat.danmaku,
// duration: videoItem.duration,
// ),
// ),
// ),
],
);
}),
),
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
);
}),
),
VideoContent(videoItem: videoItem)
],
@ -155,7 +136,6 @@ class VideoContent extends StatelessWidget {
children: [
Text(
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(fontSize: 13),
maxLines: 2,
overflow: TextOverflow.ellipsis,
@ -181,22 +161,25 @@ class VideoContent extends StatelessWidget {
type: 'color',
)
],
if (videoItem.goto == 'picture') ...[
const PBadge(
text: '动态',
stack: 'normal',
size: 'small',
type: 'line',
fs: 9,
)
],
Expanded(
child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
return SizedBox(
width: constraints.maxWidth,
child: Text(
videoItem.owner.name,
maxLines: 1,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
);
}),
child: Text(
videoItem.owner.name,
maxLines: 1,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
),
if (videoItem.goto == 'av')
SizedBox(

View File

@ -248,6 +248,9 @@ class Api {
// 移除已观看
static const String toViewDel = '/x/v2/history/toview/del';
// 清空稍后再看
static const String toViewClear = '/x/v2/history/toview/clear';
// 追番
static const String bangumiAdd = '/pgc/web/follow/add';

View File

@ -15,18 +15,9 @@ import 'package:dio_cookie_manager/dio_cookie_manager.dart';
class Request {
static final Request _instance = Request._internal();
static late CookieManager cookieManager;
static late final Dio dio;
factory Request() => _instance;
static Dio dio = Dio()
..httpClientAdapter = Http2Adapter(
ConnectionManager(
idleTimeout: const Duration(milliseconds: 10000),
// Ignore bad certificate
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
),
);
/// 设置cookie
static setCookie() async {
Box user = GStrorage.user;
@ -63,16 +54,6 @@ class Request {
dio.options.headers['cookie'] = cookieString;
}
// 移除cookie
static removeCookie() async {
await cookieManager.cookieJar
.saveFromResponse(Uri.parse(HttpString.baseUrl), []);
await cookieManager.cookieJar
.saveFromResponse(Uri.parse(HttpString.baseApiUrl), []);
cookieManager.cookieJar.deleteAll();
dio.interceptors.add(cookieManager);
}
// 从cookie中获取 csrf token
static Future<String> getCsrf() async {
var cookies = await cookieManager.cookieJar
@ -114,7 +95,16 @@ class Request {
options.headers['x-bili-aurora-zone'] = 'sh001';
options.headers['referer'] = 'https://www.bilibili.com/';
}
dio.options = options;
dio = Dio(options)
..httpClientAdapter = Http2Adapter(
ConnectionManager(
idleTimeout: const Duration(milliseconds: 10000),
// Ignore bad certificate
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
),
);
//添加拦截器
dio.interceptors.add(ApiInterceptor());

View File

@ -17,7 +17,7 @@ class ApiInterceptor extends Interceptor {
handler.next(options);
}
Box setting = GStrorage.setting;
Box user = GStrorage.user;
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
@ -29,7 +29,7 @@ class ApiInterceptor extends Interceptor {
final uri = Uri.parse(locations.first);
final accessKey = uri.queryParameters['access_key'];
final mid = uri.queryParameters['mid'];
setting.put(UserBoxKey.accessKey, {'mid': mid, 'value': accessKey});
user.put(UserBoxKey.accessKey, {'mid': mid, 'value': accessKey});
}
}
}

View File

@ -85,6 +85,12 @@ class UserHttp {
static Future<dynamic> seeYouLater() async {
var res = await Request().get(Api.seeYouLater);
if (res.data['code'] == 0) {
if (res.data['data']['count'] == 0) {
return {
'status': true,
'data': {'list': [], 'count': 0}
};
}
List<HotVideoItemModel> list = [];
for (var i in res.data['data']['list']) {
list.add(HotVideoItemModel.fromJson(i));
@ -195,4 +201,20 @@ class UserHttp {
Request().get(res.data['data']['confirm_uri']);
}
}
// 清空稍后再看
static Future toViewClear() async {
var res = await Request().post(
Api.toViewClear,
queryParameters: {
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': '操作完成'};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
}

View File

@ -18,6 +18,7 @@ import 'package:pilipala/utils/storage.dart';
/// 返回{'status': bool, 'data': List}
/// view层根据 status 判断渲染逻辑
class VideoHttp {
static Box user = GStrorage.user;
static Box setting = GStrorage.setting;
// 首页推荐视频
@ -61,7 +62,7 @@ class VideoHttp {
'pull': freshIdx == 0 ? 'true' : 'false',
'appkey': Constants.appKey,
'access_key':
setting.get(UserBoxKey.accessKey, defaultValue: {})['value'] ?? ''
user.get(UserBoxKey.accessKey, defaultValue: {})['value'] ?? ''
},
);
if (res.data['code'] == 0) {

View File

@ -478,6 +478,8 @@ class DynamicArchiveModel {
this.stat,
this.title,
this.type,
this.epid,
this.seasonId,
});
int? aid;
@ -491,6 +493,8 @@ class DynamicArchiveModel {
Stat? stat;
String? title;
int? type;
int? epid;
int? seasonId;
DynamicArchiveModel.fromJson(Map<String, dynamic> json) {
aid = json['aid'] is String ? int.parse(json['aid']) : json['aid'];
@ -503,6 +507,8 @@ class DynamicArchiveModel {
stat = json['stat'] != null ? Stat.fromJson(json['stat']) : null;
title = json['title'];
type = json['type'];
epid = json['epid'];
seasonId = json['season_id'];
}
}

View File

@ -4,12 +4,14 @@ class LatestDataModel {
this.tagName,
this.createdAt,
this.assets,
this.body,
});
String? url;
String? tagName;
String? createdAt;
List? assets;
String? body;
LatestDataModel.fromJson(Map<String, dynamic> json) {
url = json['url'];
@ -17,6 +19,7 @@ class LatestDataModel {
createdAt = json['created_at'];
assets =
json['assets'].map<AssetItem>((e) => AssetItem.fromJson(e)).toList();
body = json['body'];
}
}

View File

@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/github/latest.dart';
import 'package:pilipala/utils/utils.dart';
@ -165,7 +164,7 @@ class AboutController extends GetxController {
}
}
// 获取当前版本
// 获取当前版本
Future getCurrentApp() async {
var result = await PackageInfo.fromPlatform();
currentVersion.value = result.version;

View File

@ -22,7 +22,11 @@ import 'controller.dart';
import 'widgets/intro_detail.dart';
class BangumiIntroPanel extends StatefulWidget {
const BangumiIntroPanel({super.key});
final int? cid;
const BangumiIntroPanel({
Key? key,
this.cid,
}) : super(key: key);
@override
State<BangumiIntroPanel> createState() => _BangumiIntroPanelState();
@ -69,7 +73,11 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
);
}
} else {
return BangumiInfo(loadingStatus: true, bangumiDetail: bangumiDetail);
return BangumiInfo(
loadingStatus: true,
bangumiDetail: bangumiDetail,
cid: widget.cid,
);
}
},
);
@ -79,11 +87,13 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
class BangumiInfo extends StatefulWidget {
final bool loadingStatus;
final BangumiInfoModel? bangumiDetail;
final int? cid;
const BangumiInfo({
Key? key,
this.loadingStatus = false,
this.bangumiDetail,
this.cid,
}) : super(key: key);
@override
@ -97,6 +107,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
Box localCache = GStrorage.localCache;
late final BangumiInfoModel? bangumiItem;
late double sheetHeight;
int? cid;
@override
void initState() {
@ -105,6 +116,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
bangumiItem = bangumiIntroController.bangumiItem;
sheetHeight = localCache.get('sheetHeight');
cid = widget.cid!;
}
// 收藏
@ -320,9 +332,10 @@ class _BangumiInfoState extends State<BangumiInfo> {
pages: bangumiItem != null
? bangumiItem!.episodes!
: widget.bangumiDetail!.episodes!,
cid: bangumiItem != null
? bangumiItem!.episodes!.first.cid
: widget.bangumiDetail!.episodes!.first.cid,
cid: cid ??
(bangumiItem != null
? bangumiItem!.episodes!.first.cid
: widget.bangumiDetail!.episodes!.first.cid),
sheetHeight: sheetHeight,
changeFuc: (bvid, cid, aid) => bangumiIntroController
.changeSeasonOrbangu(bvid, cid, aid),

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@ -36,10 +37,10 @@ class _BangumiPageState extends State<BangumiPage>
() async {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
if (!_bangumidController.isLoadingMore) {
EasyThrottle.throttle('my-throttler', const Duration(seconds: 1), () {
_bangumidController.isLoadingMore = true;
await _bangumidController.onLoad();
}
_bangumidController.onLoad();
});
}
final ScrollDirection direction =

View File

@ -22,77 +22,106 @@ class BangumiPanel extends StatefulWidget {
class _BangumiPanelState extends State<BangumiPanel> {
late int currentIndex;
final ScrollController listViewScrollCtr = ScrollController();
final ScrollController listViewScrollCtr_2 = ScrollController();
@override
void initState() {
super.initState();
currentIndex = widget.pages.indexWhere((e) => e.cid == widget.cid!);
scrollToIndex();
}
@override
void dispose() {
listViewScrollCtr.dispose();
listViewScrollCtr_2.dispose();
super.dispose();
}
void showBangumiPanel() {
showBottomSheet(
context: context,
builder: (_) => Container(
height: widget.sheetHeight,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 200));
listViewScrollCtr_2.animateTo(currentIndex * 56,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut);
});
// 在这里使用 setState 更新状态
return Container(
height: widget.sheetHeight,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Text(
'合集(${widget.pages.length}',
style: Theme.of(context).textTheme.titleMedium,
AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'合集(${widget.pages.length}',
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
titleSpacing: 10,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
Expanded(
child: Material(
child: ListView.builder(
controller: listViewScrollCtr_2,
itemCount: widget.pages.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
setState(() {
changeFucCall(widget.pages[index], index);
});
},
dense: false,
leading: index == currentIndex
? Image.asset(
'assets/images/live.gif',
color:
Theme.of(context).colorScheme.primary,
height: 12,
)
: null,
title: Text(
'${index + 1}${widget.pages[index].longTitle!}',
style: TextStyle(
fontSize: 14,
color: index == currentIndex
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
trailing: widget.pages[index].badge != null
? Image.asset(
'assets/images/big-vip.png',
height: 20,
)
: const SizedBox(),
);
},
),
),
),
],
),
titleSpacing: 10,
),
Expanded(
child: Material(
child: ListView.builder(
itemCount: widget.pages.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () => changeFucCall(widget.pages[index], index),
dense: false,
leading: index == currentIndex
? Image.asset(
'assets/images/live.gif',
color: Theme.of(context).colorScheme.primary,
height: 12,
)
: null,
title: Text(
'${index + 1}${widget.pages[index].longTitle!}',
style: TextStyle(
fontSize: 14,
color: index == currentIndex
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
trailing: widget.pages[index].badge != null
? Image.asset(
'assets/images/big-vip.png',
height: 20,
)
: const SizedBox(),
);
},
),
),
),
],
),
),
);
},
);
},
);
}
@ -108,6 +137,15 @@ class _BangumiPanelState extends State<BangumiPanel> {
);
currentIndex = i;
setState(() {});
scrollToIndex();
}
void scrollToIndex() {
WidgetsBinding.instance.addPostFrameCallback((_) {
// 在回调函数中获取更新后的状态
listViewScrollCtr.animateTo(currentIndex * 150,
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
});
}
@override
@ -150,6 +188,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
SizedBox(
height: 60,
child: ListView.builder(
controller: listViewScrollCtr,
scrollDirection: Axis.horizontal,
itemCount: widget.pages.length,
itemExtent: 150,
@ -222,87 +261,6 @@ class _BangumiPanelState extends State<BangumiPanel> {
);
})),
)
// SingleChildScrollView(
// padding: const EdgeInsets.only(top: 7, bottom: 7),
// scrollDirection: Axis.horizontal,
// child: ConstrainedBox(
// constraints: BoxConstraints(
// minWidth: MediaQuery.of(context).size.width,
// ),
// child: Row(
// children: [
// for (int i = 0; i < widget.pages.length; i++) ...[
// Container(
// width: 150,
// margin: const EdgeInsets.only(right: 10),
// child: Material(
// color: Theme.of(context).colorScheme.onInverseSurface,
// borderRadius: BorderRadius.circular(6),
// clipBehavior: Clip.hardEdge,
// child: InkWell(
// onTap: () => changeFucCall(widget.pages[i], i),
// child: Padding(
// padding: const EdgeInsets.symmetric(
// vertical: 8, horizontal: 10),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// children: [
// if (i == currentIndex) ...[
// Image.asset(
// 'assets/images/live.gif',
// color:
// Theme.of(context).colorScheme.primary,
// height: 12,
// ),
// const SizedBox(width: 6)
// ],
// Text(
// '第${i + 1}话',
// style: TextStyle(
// fontSize: 13,
// color: i == currentIndex
// ? Theme.of(context)
// .colorScheme
// .primary
// : Theme.of(context)
// .colorScheme
// .onSurface),
// ),
// const SizedBox(width: 2),
// if (widget.pages[i].badge != null) ...[
// Image.asset(
// 'assets/images/big-vip.png',
// height: 16,
// ),
// ],
// ],
// ),
// const SizedBox(height: 3),
// Text(
// widget.pages[i].longTitle!,
// maxLines: 1,
// style: TextStyle(
// fontSize: 13,
// color: i == currentIndex
// ? Theme.of(context).colorScheme.primary
// : Theme.of(context)
// .colorScheme
// .onSurface),
// overflow: TextOverflow.ellipsis,
// )
// ],
// ),
// ),
// ),
// ),
// ),
// ]
// ],
// ),
// ),
// )
],
);
}

View File

@ -3,13 +3,18 @@
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/dynamics.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/models/common/dynamics_type.dart';
import 'package:pilipala/models/common/search_type.dart';
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/storage.dart';
import 'package:pilipala/utils/utils.dart';
class DynamicsController extends GetxController {
int page = 1;
@ -46,8 +51,19 @@ class DynamicsController extends GetxController {
];
bool flag = false;
RxInt initialValue = 1.obs;
Box user = GStrorage.user;
RxBool userLogin = false.obs;
@override
void onInit() {
userLogin.value = user.get(UserBoxKey.userLogin, defaultValue: false);
super.onInit();
}
Future queryFollowDynamic({type = 'init'}) async {
if (!userLogin.value) {
return {'status': false, 'msg': '账号未登录'};
}
if (type == 'init') {
dynamicsList.clear();
}
@ -142,10 +158,42 @@ class DynamicsController extends GetxController {
/// TODO
case 'DYNAMIC_TYPE_UGC_SEASON':
print('合集');
break;
case 'DYNAMIC_TYPE_PGC_UNION':
print('DYNAMIC_TYPE_PGC_UNION 番剧');
DynamicArchiveModel pgc = item.modules.moduleDynamic.major.pgc;
if (pgc.epid != null) {
SmartDialog.showLoading(msg: '获取中...');
var res = await SearchHttp.bangumiInfo(epId: pgc.epid);
SmartDialog.dismiss();
if (res['status']) {
EpisodeItem episode = res['data'].episodes.first;
String bvid = episode.bvid!;
int cid = episode.cid!;
String pic = episode.cover!;
String heroTag = Utils.makeHeroTag(cid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}',
arguments: {
'pic': pic,
'heroTag': heroTag,
'videoType': SearchType.media_bangumi,
'bangumiItem': res['data'],
},
);
}
}
break;
}
}
Future queryFollowUp() async {
Future queryFollowUp({type = 'init'}) async {
if (!userLogin.value) {
return {'status': false, 'msg': '账号未登录'};
}
if (type == 'init') {
upData = FollowUpModel().obs;
}
var res = await DynamicsHttp.followUp();
if (res['status']) {
upData.value = res['data'];

View File

@ -9,6 +9,7 @@ import 'package:pilipala/common/skeleton/dynamic_card.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
@ -30,6 +31,7 @@ class _DynamicsPageState extends State<DynamicsPage>
late Future _futureBuilderFutureUp;
bool _isLoadingMore = false;
Box user = GStrorage.user;
EventBus eventBus = EventBus();
@override
bool get wantKeepAlive => true;
@ -62,6 +64,14 @@ class _DynamicsPageState extends State<DynamicsPage>
}
},
);
eventBus.on(EventName.loginEvent, (args) {
_dynamicsController.userLogin.value = args['status'];
setState(() {
_futureBuilderFuture = _dynamicsController.queryFollowDynamic();
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
});
});
}
@override
@ -107,71 +117,82 @@ class _DynamicsPageState extends State<DynamicsPage>
return const SizedBox();
}
}),
Obx(() => Visibility(
visible: _dynamicsController.mid.value == -1,
child: CustomSlidingSegmentedControl<int>(
initialValue: _dynamicsController.initialValue.value,
children: {
1: Text(
'全部',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
Obx(
() => _dynamicsController.userLogin.value
? Visibility(
visible: _dynamicsController.mid.value == -1,
child: CustomSlidingSegmentedControl<int>(
initialValue:
_dynamicsController.initialValue.value,
children: {
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)),
// 4: 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);
},
),
2: Text('投稿',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize)),
3: Text('番剧',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize)),
// 4: 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),
)
],
),
Positioned(
right: 4,
top: 0,
bottom: 0,
child: IconButton(
padding: EdgeInsets.zero,
onPressed: () =>
{feedBack(), _dynamicsController.resetSearch()},
icon: const Icon(Icons.history, size: 21),
Obx(
() => Visibility(
visible: _dynamicsController.userLogin.value,
child: Positioned(
right: 4,
top: 0,
bottom: 0,
child: IconButton(
padding: EdgeInsets.zero,
onPressed: () =>
{feedBack(), _dynamicsController.resetSearch()},
icon: const Icon(Icons.history, size: 21),
),
),
),
),
],
@ -224,7 +245,14 @@ class _DynamicsPageState extends State<DynamicsPage>
} else {
return HttpError(
errMsg: data['msg'],
fn: () => _dynamicsController.onRefresh(),
fn: () {
setState(() {
_futureBuilderFuture =
_dynamicsController.queryFollowDynamic();
_futureBuilderFutureUp =
_dynamicsController.queryFollowUp();
});
},
);
}
} else {

View File

@ -36,7 +36,8 @@ Widget author(item, context) {
Text(
item.modules.moduleAuthor.name,
style: TextStyle(
color: item.modules.moduleAuthor!.vip!['status'] > 0
color: item.modules.moduleAuthor!.vip != null &&
item.modules.moduleAuthor!.vip['status'] > 0
? const Color.fromARGB(255, 251, 100, 163)
: Theme.of(context).colorScheme.onBackground,
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,

View File

@ -9,9 +9,10 @@ import 'package:pilipala/utils/storage.dart';
class HistoryController extends GetxController {
final ScrollController scrollController = ScrollController();
RxList<HisListItem> historyList = [HisListItem()].obs;
bool isLoadingMore = false;
RxBool isLoadingMore = false.obs;
RxBool pauseStatus = false.obs;
Box localCache = GStrorage.localCache;
RxBool isLoading = false.obs;
@override
void onInit() {
@ -26,9 +27,9 @@ class HistoryController extends GetxController {
max = historyList.last.history!.oid!;
viewAt = historyList.last.viewAt!;
}
isLoadingMore = true;
isLoadingMore.value = true;
var res = await UserHttp.historyList(max, viewAt);
isLoadingMore = false;
isLoadingMore.value = false;
if (res['status']) {
if (type == 'onload') {
historyList.addAll(res['data'].list);

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/pages/history/index.dart';
import 'widgets/item.dart';
@ -27,7 +28,7 @@ class _HistoryPageState extends State<HistoryPage> {
if (_historyController.scrollController.position.pixels >=
_historyController.scrollController.position.maxScrollExtent -
300) {
if (!_historyController.isLoadingMore) {
if (!_historyController.isLoadingMore.value) {
_historyController.onLoad();
}
}
@ -92,13 +93,9 @@ class _HistoryPageState extends State<HistoryPage> {
Map data = snapshot.data;
if (data['status']) {
return Obx(
() => _historyController.historyList.isEmpty
? const SliverToBoxAdapter(
child: Center(
child: Text('没数据'),
),
)
: SliverList(
() => _historyController.historyList.isNotEmpty &&
!_historyController.isLoadingMore.value
? SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return HistoryItem(
@ -108,7 +105,12 @@ class _HistoryPageState extends State<HistoryPage> {
},
childCount:
_historyController.historyList.length),
),
)
: _historyController.isLoadingMore.value
? const SliverToBoxAdapter(
child: Center(child: Text('加载中')),
)
: const NoData(),
);
} else {
return HttpError(

View File

@ -5,6 +5,7 @@ import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/models/common/business_type.dart';
@ -115,7 +116,7 @@ class HistoryItem extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.cardSpace, 5, StyleString.cardSpace, 5),
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (context, boxConstraints) {
double width =
@ -232,6 +233,7 @@ class VideoContent extends StatelessWidget {
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
Utils.dateFormat(videoItem.viewAt!),
@ -239,7 +241,46 @@ class VideoContent extends StatelessWidget {
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline),
)
),
if (videoItem.badge != '番剧' &&
!videoItem.tagName.contains('动画') &&
videoItem.history.business != 'live' &&
!videoItem.history.business.contains('article'))
SizedBox(
width: 24,
height: 24,
child: PopupMenuButton<String>(
padding: EdgeInsets.zero,
tooltip: '稍后再看',
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: 14,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
onTap: () async {
var res = await UserHttp.toViewLater(
bvid: videoItem.history.bvid);
SmartDialog.showToast(res['msg']);
},
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
),
),
],
),
),
],
),
],

View File

@ -126,7 +126,9 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
child: OverlayPop(videoItem: videoItem),
closeFn: _hotController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem, closeFn: _hotController.popupDialog?.remove),
),
);
}

View File

@ -6,15 +6,20 @@ import 'package:pilipala/models/model_hot_video_item.dart';
class LaterController extends GetxController {
final ScrollController scrollController = ScrollController();
RxList<HotVideoItemModel> laterList = [HotVideoItemModel()].obs;
RxList<HotVideoItemModel> laterList = <HotVideoItemModel>[].obs;
int count = 0;
RxBool isLoading = false.obs;
Future queryLaterList() async {
isLoading.value = true;
var res = await UserHttp.seeYouLater();
if (res['status']) {
laterList.value = res['data']['list'];
count = res['data']['count'];
if (count > 0) {
laterList.value = res['data']['list'];
}
}
isLoading.value = false;
return res;
}
@ -47,4 +52,34 @@ class LaterController extends GetxController {
},
);
}
// 一键清空
Future toViewClear() async {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('清空确认'),
content: const Text('确定要清空你的稍后再看列表吗?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: const Text('取消')),
TextButton(
onPressed: () async {
var res = await UserHttp.toViewClear();
if (res['status']) {
laterList.clear();
}
SmartDialog.dismiss();
SmartDialog.showToast(res['msg']);
},
child: const Text('确认'),
)
],
);
},
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/pages/later/index.dart';
@ -29,25 +30,38 @@ class _LaterPageState extends State<LaterPage> {
titleSpacing: 0,
centerTitle: false,
title: Obx(
() => Text(
'稍后再看 (${_laterController.laterList.length}/100)',
style: Theme.of(context).textTheme.titleMedium,
),
() => _laterController.laterList.isNotEmpty
? Text(
'稍后再看 (${_laterController.laterList.length}/100)',
style: Theme.of(context).textTheme.titleMedium,
)
: Text(
'稍后再看',
style: Theme.of(context).textTheme.titleMedium,
),
),
actions: [
TextButton(
onPressed: () => _laterController.toViewDel(),
child: const Text('移除已看'),
Obx(
() => _laterController.laterList.isNotEmpty
? TextButton(
onPressed: () => _laterController.toViewDel(),
child: const Text('移除已看'),
)
: const SizedBox(),
),
Obx(
() => _laterController.laterList.isNotEmpty
? IconButton(
tooltip: '一键清空',
onPressed: () => _laterController.toViewClear(),
icon: Icon(
Icons.clear_all_outlined,
size: 21,
color: Theme.of(context).colorScheme.primary,
),
)
: const SizedBox(),
),
// IconButton(
// tooltip: '一键清空',
// onPressed: () {},
// icon: Icon(
// Icons.clear_all_outlined,
// size: 21,
// color: Theme.of(context).colorScheme.primary,
// ),
// ),
const SizedBox(width: 8),
],
),
@ -61,19 +75,29 @@ class _LaterPageState extends State<LaterPage> {
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return VideoCardH(
videoItem: _laterController.laterList[index],
source: 'later',
);
}, childCount: _laterController.laterList.length),
),
() => _laterController.laterList.isNotEmpty &&
!_laterController.isLoading.value
? SliverList(
delegate:
SliverChildBuilderDelegate((context, index) {
return VideoCardH(
videoItem: _laterController.laterList[index],
source: 'later',
);
}, childCount: _laterController.laterList.length),
)
: _laterController.isLoading.value
? const SliverToBoxAdapter(
child: Center(child: Text('加载中')),
)
: const NoData(),
);
} else {
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
fn: () => setState(() {
_futureBuilderFuture = _laterController.queryLaterList();
}),
);
}
} else {
@ -81,7 +105,7 @@ class _LaterPageState extends State<LaterPage> {
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoCardHSkeleton();
}, childCount: 5),
}, childCount: 10),
);
}
},

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@ -35,10 +36,10 @@ class _LivePageState extends State<LivePage> {
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
if (!_liveController.isLoadingMore) {
EasyThrottle.throttle('my-throttler', const Duration(seconds: 1), () {
_liveController.isLoadingMore = true;
_liveController.onLoad();
}
});
}
final ScrollDirection direction =
@ -113,7 +114,9 @@ class _LivePageState extends State<LivePage> {
OverlayEntry _createPopupDialog(liveItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
child: OverlayPop(videoItem: liveItem),
closeFn: _liveController.popupDialog?.remove,
child: OverlayPop(
videoItem: liveItem, closeFn: _liveController.popupDialog?.remove),
),
);
}

View File

@ -35,11 +35,11 @@ class LiveCardV extends StatelessWidget {
longPress!();
}
},
onLongPressEnd: (details) {
if (longPressEnd != null) {
longPressEnd!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}',

View File

@ -74,12 +74,10 @@ class LiveRoomController extends GetxController {
if (value == 0) {
// 设置音量
volumeOff.value = false;
// meeduPlayerController.setVolume(volume);
} else {
// 取消音量
volume = value;
volumeOff.value = true;
// meeduPlayerController.setVolume(0);
}
}
}

View File

@ -2,9 +2,12 @@ import 'dart:async';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/view.dart';
import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart';
class MainController extends GetxController {
List<Widget> pages = <Widget>[
@ -49,4 +52,13 @@ class MainController extends GetxController {
].obs;
final StreamController<bool> bottomBarStream =
StreamController<bool>.broadcast();
Box setting = GStrorage.setting;
@override
void onInit() {
super.onInit();
if (setting.get(SettingBoxKey.autoUpdate, defaultValue: false)) {
Utils.checkUpdata();
}
}
}

View File

@ -4,6 +4,7 @@ import 'package:hive/hive.dart';
import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
import './controller.dart';
@ -96,6 +97,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
@override
void dispose() async {
await GStrorage.close();
EventBus().off(EventName.loginEvent);
super.dispose();
}

View File

@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/utils.dart';
class MediaPage extends StatefulWidget {
@ -16,6 +17,7 @@ class _MediaPageState extends State<MediaPage>
with AutomaticKeepAliveClientMixin {
late MediaController mediaController;
late Future _futureBuilderFuture;
EventBus eventBus = EventBus();
@override
bool get wantKeepAlive => true;
@ -25,6 +27,12 @@ class _MediaPageState extends State<MediaPage>
super.initState();
mediaController = Get.put(MediaController());
_futureBuilderFuture = mediaController.queryFavFolder();
eventBus.on(EventName.loginEvent, (args) {
mediaController.userLogin.value = args['status'];
setState(() {
_futureBuilderFuture = mediaController.queryFavFolder();
});
});
}
@override
@ -68,7 +76,7 @@ class _MediaPageState extends State<MediaPage>
),
),
],
Obx(() => mediaController.userLogin.value == true
Obx(() => mediaController.userLogin.value
? favFolder(mediaController, context)
: const SizedBox())
],
@ -116,7 +124,11 @@ class _MediaPageState extends State<MediaPage>
),
),
trailing: IconButton(
onPressed: () => _futureBuilderFuture,
onPressed: () {
setState(() {
_futureBuilderFuture = mediaController.queryFavFolder();
});
},
icon: const Icon(
Icons.refresh,
size: 20,
@ -149,11 +161,25 @@ class _MediaPageState extends State<MediaPage>
right: 14, bottom: 35),
child: Center(
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith(
(states) {
return Theme.of(context)
.colorScheme
.primaryContainer
.withOpacity(0.5);
}),
),
onPressed: () => Get.toNamed('/fav'),
icon: Icon(
Icons.arrow_forward_ios,
size: 18,
color: Theme.of(context).primaryColor,
color: Theme.of(context)
.colorScheme
.primary,
),
),
));

View File

@ -32,9 +32,6 @@ class MineController extends GetxController {
onLogin() async {
if (!userLogin.value) {
/// TODO
Get.back();
await Future.delayed(const Duration(milliseconds: 150));
Get.toNamed(
'/webview',
parameters: {

View File

@ -6,15 +6,37 @@ import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/common/theme_type.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'controller.dart';
class MinePage extends StatelessWidget {
class MinePage extends StatefulWidget {
const MinePage({super.key});
@override
Widget build(BuildContext context) {
final MineController mineController = Get.put(MineController());
State<MinePage> createState() => _MinePageState();
}
class _MinePageState extends State<MinePage> {
final MineController mineController = Get.put(MineController());
late Future _futureBuilderFuture;
EventBus eventBus = EventBus();
@override
void initState() {
super.initState();
_futureBuilderFuture = mineController.queryUserInfo();
eventBus.on(EventName.loginEvent, (args) {
mineController.userLogin.value = args['status'];
if (mounted) {
setState(() {
_futureBuilderFuture = mineController.queryUserInfo();
});
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
@ -61,7 +83,7 @@ class MinePage extends StatelessWidget {
children: [
const SizedBox(height: 10),
FutureBuilder(
future: mineController.queryUserInfo(),
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data['status']) {

View File

@ -8,9 +8,8 @@ import 'package:pilipala/utils/storage.dart';
class RcmdController extends GetxController {
final ScrollController scrollController = ScrollController();
int _currentPage = 0;
int crossAxisCount = 2;
RxList<RecVideoItemAppModel> videoList = [RecVideoItemAppModel()].obs;
bool isLoadingMore = false;
RxList<RecVideoItemAppModel> videoList = <RecVideoItemAppModel>[].obs;
bool isLoadingMore = true;
OverlayEntry? popupDialog;
Box recVideo = GStrorage.recVideo;
@ -29,6 +28,9 @@ class RcmdController extends GetxController {
// 获取推荐
Future queryRcmdFeed(type) async {
if (isLoadingMore == false) {
return;
}
if (type == 'onRefresh') {
_currentPage = 0;
}
@ -37,7 +39,7 @@ class RcmdController extends GetxController {
);
if (res['status']) {
if (type == 'init') {
if (videoList.length > 1) {
if (videoList.isNotEmpty) {
videoList.addAll(res['data']);
} else {
videoList.value = res['data'];
@ -56,6 +58,7 @@ class RcmdController extends GetxController {
// 下拉刷新
Future onRefresh() async {
isLoadingMore = true;
queryRcmdFeed('onRefresh');
}

View File

@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@ -39,10 +41,10 @@ class _RcmdPageState extends State<RcmdPage>
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
if (!_rcmdController.isLoadingMore) {
EasyThrottle.throttle('my-throttler', const Duration(seconds: 1), () {
_rcmdController.isLoadingMore = true;
_rcmdController.onLoad();
}
});
}
final ScrollDirection direction =
@ -74,30 +76,36 @@ class _RcmdPageState extends State<RcmdPage>
controller: _rcmdController.scrollController,
slivers: [
SliverPadding(
// 单列布局 EdgeInsets.zero
padding: _rcmdController.crossAxisCount == 1
? EdgeInsets.zero
: const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0),
padding:
const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0),
sliver: FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
return SliverLayoutBuilder(
builder: (context, boxConstraints) {
return Obx(() => contentGrid(
_rcmdController, _rcmdController.videoList));
});
return Platform.isAndroid || Platform.isIOS
? Obx(() => contentGrid(
_rcmdController, _rcmdController.videoList))
: SliverLayoutBuilder(
builder: (context, boxConstraints) {
return Obx(() => contentGrid(
_rcmdController, _rcmdController.videoList));
});
} else {
return HttpError(
errMsg: data['msg'],
fn: () => {},
fn: () {
setState(() {
_futureBuilderFuture =
_rcmdController.queryRcmdFeed('init');
});
},
);
}
} else {
// 缓存数据
if (_rcmdController.videoList.length > 1) {
if (_rcmdController.videoList.isNotEmpty) {
return contentGrid(
_rcmdController, _rcmdController.videoList);
}
@ -118,9 +126,12 @@ class _RcmdPageState extends State<RcmdPage>
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
child: OverlayPop(videoItem: videoItem),
));
builder: (context) => AnimatedDialog(
closeFn: _rcmdController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem, closeFn: _rcmdController.popupDialog?.remove),
),
);
}
Widget contentGrid(ctr, videoList) {

View File

@ -1,9 +1,12 @@
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/init.dart';
import 'package:pilipala/models/common/theme_type.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/mine/controller.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
@ -30,12 +33,47 @@ class SettingController extends GetxController {
}
loginOut() async {
await Request.removeCookie();
await Get.find<MineController>().resetUserInfo();
userLogin.value = user.get(UserBoxKey.userLogin) ?? false;
userInfoCache.put('userInfoCache', null);
HomeController homeCtr = Get.find<HomeController>();
homeCtr.updateLoginStatus(false);
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('确认要退出登录吗'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: const Text('点错了'),
),
TextButton(
onPressed: () async {
// 清空cookie
await Request.cookieManager.cookieJar.deleteAll();
Request.dio.options.headers['cookie'] = '';
// 清空本地存储的用户标识
userInfoCache.put('userInfoCache', null);
user.put(UserBoxKey.accessKey, {'mid': -1, 'value': ''});
// 更改我的页面登录状态
await Get.find<MineController>().resetUserInfo();
// 更改主页登录状态
HomeController homeCtr = Get.find<HomeController>();
homeCtr.updateLoginStatus(false);
// 事件通知
EventBus eventBus = EventBus();
eventBus.emit(EventName.loginEvent, {'status': false});
SmartDialog.dismiss().then((value) => Get.back());
},
child: const Text('确认'),
)
],
);
},
);
}
// 开启关闭震动反馈

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:pilipala/utils/storage.dart';
import 'widgets/switch_item.dart';
class ExtraSetting extends StatefulWidget {
const ExtraSetting({super.key});
@override
State<ExtraSetting> createState() => _ExtraSettingState();
}
class _ExtraSettingState extends State<ExtraSetting> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
title: Text(
'其他设置',
style: Theme.of(context).textTheme.titleMedium,
),
),
body: ListView(
children: const [
SetSwitchItem(
title: '检查更新',
subTitle: '每次启动时检查是否需要更新',
setKey: SettingBoxKey.autoUpdate,
defaultVal: false,
),
],
),
);
}
}

View File

@ -34,11 +34,11 @@ class SettingPage extends StatelessWidget {
dense: false,
title: const Text('外观设置'),
),
// ListTile(
// onTap: () {},
// dense: false,
// title: const Text('其他设置'),
// ),
ListTile(
onTap: () => Get.toNamed('/extraSetting'),
dense: false,
title: const Text('其他设置'),
),
Obx(
() => Visibility(
visible: settingController.userLogin.value,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart';
class SetSwitchItem extends StatefulWidget {
final String? title;
@ -61,6 +62,9 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
onChanged: (value) {
val = value;
Setting.put(widget.setKey, value);
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
Utils.checkUpdata();
}
setState(() {});
}),
),

View File

@ -414,6 +414,7 @@ class VideoIntroController extends GetxController {
videoReplyCtr.queryReplyList(type: 'init');
} catch (_) {}
this.bvid = bvid;
lastPlayCid.value = cid;
await queryVideoIntro();
}
}

View File

@ -270,12 +270,16 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
// 合集
if (!loadingStatus &&
widget.videoDetail!.ugcSeason != null) ...[
SeasonPanel(
ugcSeason: widget.videoDetail!.ugcSeason!,
cid: widget.videoDetail!.pages!.first.cid,
sheetHeight: sheetHeight,
changeFuc: (bvid, cid, aid) => videoIntroController
.changeSeasonOrbangu(bvid, cid, aid),
Obx(
() => SeasonPanel(
ugcSeason: widget.videoDetail!.ugcSeason!,
cid: videoIntroController.lastPlayCid.value != 0
? videoIntroController.lastPlayCid.value
: widget.videoDetail!.pages!.first.cid,
sheetHeight: sheetHeight,
changeFuc: (bvid, cid, aid) => videoIntroController
.changeSeasonOrbangu(bvid, cid, aid),
),
)
],
if (!loadingStatus &&

View File

@ -38,6 +38,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
item.cid,
item.aid,
);
currentIndex = i;
Get.back();
}

View File

@ -6,71 +6,74 @@ import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
import './controller.dart';
class RelatedVideoPanel extends GetView<ReleatedController> {
class RelatedVideoPanel extends StatefulWidget {
const RelatedVideoPanel({super.key});
@override
State<RelatedVideoPanel> createState() => _RelatedVideoPanelState();
}
class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
final ReleatedController _releatedController =
Get.put(ReleatedController(), tag: Get.arguments['heroTag']);
@override
Widget build(BuildContext context) {
return GetBuilder(
init: ReleatedController(),
id: Get.arguments['heroTag'],
builder: (context) {
return FutureBuilder(
future: ReleatedController().queryRelatedVideo(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data!['status']) {
// 请求成功
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
if (index == snapshot.data['data'].length) {
return SizedBox(
height: MediaQuery.of(context).padding.bottom);
} else {
return Material(
child: VideoCardH(
videoItem: snapshot.data['data'][index],
longPress: () {
try {
ReleatedController().popupDialog =
_createPopupDialog(
snapshot.data['data'][index]);
Overlay.of(context)
.insert(ReleatedController().popupDialog!);
} catch (_) {
return {};
}
},
longPressEnd: () {
ReleatedController().popupDialog?.remove();
},
),
);
}
}, childCount: snapshot.data['data'].length + 1));
} else {
// 请求错误
return const Center(
child: Text('出错了'),
);
}
return FutureBuilder(
future: _releatedController.queryRelatedVideo(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data!['status']) {
// 请求成功
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
if (index == snapshot.data['data'].length) {
return SizedBox(height: MediaQuery.of(context).padding.bottom);
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoCardHSkeleton();
}, childCount: 5),
return Material(
child: VideoCardH(
videoItem: snapshot.data['data'][index],
longPress: () {
try {
_releatedController.popupDialog =
_createPopupDialog(snapshot.data['data'][index]);
Overlay.of(context)
.insert(_releatedController.popupDialog!);
} catch (err) {
return {};
}
},
longPressEnd: () {
_releatedController.popupDialog?.remove();
},
),
);
}
},
}, childCount: snapshot.data['data'].length + 1));
} else {
// 请求错误
return const Center(
child: Text('出错了'),
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoCardHSkeleton();
}, childCount: 5),
);
});
}
},
);
}
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
child: OverlayPop(videoItem: videoItem),
closeFn: _releatedController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem,
closeFn: _releatedController.popupDialog?.remove),
),
);
}

View File

@ -37,6 +37,9 @@ class VideoReplyController extends GetxController {
if (type == 'init') {
currentPage = 0;
}
if (noMore.value == '没有更多了') {
return;
}
var res = await ReplyHttp.replyList(
oid: aid!,
pageNum: currentPage + 1,

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@ -65,9 +66,9 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
if (_videoReplyController.scrollController.position.pixels >=
_videoReplyController.scrollController.position.maxScrollExtent -
300) {
if (!_videoReplyController.isLoadingMore) {
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
_videoReplyController.onLoad();
}
});
}
final ScrollDirection direction =

View File

@ -322,7 +322,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
const VideoIntroPanel(),
] else if (videoDetailController.videoType ==
SearchType.media_bangumi) ...[
const BangumiIntroPanel()
BangumiIntroPanel(
cid: videoDetailController.cid)
],
// if (videoDetailController.videoType ==
// SearchType.video) ...[

View File

@ -1,18 +1,15 @@
// ignore_for_file: avoid_print
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/constants.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/mine/index.dart';
import 'package:pilipala/pages/rcmd/controller.dart';
import 'package:pilipala/utils/cookie.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:webview_cookie_manager/webview_cookie_manager.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebviewController extends GetxController {
@ -22,6 +19,7 @@ class WebviewController extends GetxController {
final WebViewController controller = WebViewController();
RxInt loadProgress = 0.obs;
RxBool loadShow = true.obs;
EventBus eventBus = EventBus();
@override
void onInit() {
@ -30,17 +28,17 @@ class WebviewController extends GetxController {
type = Get.parameters['type']!;
pageTitle = Get.parameters['pageTitle']!;
webviewInit();
if (type == 'login') {
controller.clearCache();
controller.clearLocalStorage();
WebViewCookieManager().clearCookies();
controller.setUserAgent(Request().headerUa('mob'));
}
webviewInit();
}
webviewInit() {
controller
..setUserAgent(Request().headerUa('mob'))
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
@ -51,23 +49,15 @@ class WebviewController extends GetxController {
},
onPageStarted: (String url) {},
// 加载完成
onPageFinished: (String url) async {
onUrlChange: (UrlChange urlChange) async {
loadShow.value = false;
String url = urlChange.url ?? '';
if (type == 'login' &&
(url.startsWith(
'https://passport.bilibili.com/web/sso/exchange_cookie') ||
url.startsWith('https://m.bilibili.com/'))) {
try {
var cookies =
await WebviewCookieManager().getCookies(HttpString.baseUrl);
var apiCookies = await WebviewCookieManager()
.getCookies(HttpString.baseApiUrl);
var tCookies =
await WebviewCookieManager().getCookies(HttpString.tUrl);
await SetCookie.onSet(cookies, HttpString.baseUrl);
await SetCookie.onSet(apiCookies, HttpString.baseApiUrl);
await SetCookie.onSet(tCookies, HttpString.tUrl);
await UserHttp.userInfo();
await SetCookie.onSet();
var result = await UserHttp.userInfo();
UserHttp.thirdLogin();
print('网页登录: $result');
@ -83,15 +73,29 @@ class WebviewController extends GetxController {
Box userInfoCache = GStrorage.userInfo;
userInfoCache.put('userInfoCache', result['data']);
Get.find<MineController>().userInfo.value = result['data'];
Get.find<MineController>().onInit();
Get.find<RcmdController>().queryRcmdFeed('onRefresh');
Get.find<DynamicsController>().queryFollowDynamic();
// 通知更新
eventBus.emit(EventName.loginEvent, {'status': true});
HomeController homeCtr = Get.find<HomeController>();
homeCtr.updateLoginStatus(true);
} catch (_) {}
} catch (err) {
SmartDialog.show(builder: (context) {
return AlertDialog(
title: const Text('登录遇到问题'),
content: Text(err.toString()),
actions: [
TextButton(
onPressed: () => controller.reload(),
child: const Text('确认'),
)
],
);
});
}
Get.back();
} else {
// 获取用户信息失败
SmartDialog.showToast(result.msg);
}
} catch (e) {
print(e);

View File

@ -4,6 +4,8 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:media_kit/media_kit.dart';
@ -14,7 +16,6 @@ import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:universal_platform/universal_platform.dart';
import 'package:volume_controller/volume_controller.dart';
// import 'package:wakelock_plus/wakelock_plus.dart';
import 'models/data_status.dart';
@ -68,7 +69,7 @@ class PlPlayerController {
final Rx<String> _direction = 'horizontal'.obs;
Rx<bool> videoFitChanged = false.obs;
final Rx<BoxFit> _videoFit = Rx(BoxFit.fill);
final Rx<BoxFit> _videoFit = Rx(BoxFit.contain);
///
// ignore: prefer_final_fields
@ -93,13 +94,13 @@ class PlPlayerController {
// final Durations durations;
List<BoxFit> fits = [
BoxFit.contain,
BoxFit.cover,
BoxFit.fill,
BoxFit.fitHeight,
BoxFit.fitWidth,
BoxFit.scaleDown
List<Map<String, dynamic>> videoFitType = [
{'attr': BoxFit.contain, 'desc': '包含'},
{'attr': BoxFit.cover, 'desc': '覆盖'},
{'attr': BoxFit.fill, 'desc': '填充'},
{'attr': BoxFit.fitHeight, 'desc': '高度适应'},
{'attr': BoxFit.fitWidth, 'desc': '宽度适应'},
{'attr': BoxFit.scaleDown, 'desc': '缩小适应'},
];
PreferredSizeWidget? headerControl;
@ -568,7 +569,7 @@ class PlPlayerController {
Future<void> getCurrentVolume() async {
// mac try...catch
try {
_currentVolume.value = await VolumeController().getVolume();
_currentVolume.value = (await FlutterVolumeController.getVolume())!;
} catch (_) {}
}
@ -585,7 +586,8 @@ class PlPlayerController {
volume.value = volumeNew;
try {
VolumeController().setVolume(volumeNew, showSystemUI: false);
FlutterVolumeController.showSystemUI = false;
await FlutterVolumeController.setVolume(volumeNew);
} catch (err) {
print(err);
}
@ -631,10 +633,17 @@ class PlPlayerController {
void toggleVideoFit() {
videoFitChangedTimer?.cancel();
videoFitChanged.value = true;
if (fits.indexOf(_videoFit.value) < fits.length - 1) {
_videoFit.value = fits[fits.indexOf(_videoFit.value) + 1];
// 范围内
List attrs = videoFitType.map((e) => e['attr']).toList();
if (attrs.indexOf(_videoFit.value) < attrs.length - 1) {
int index = attrs.indexOf(_videoFit.value);
_videoFit.value = attrs[index + 1];
print(videoFitType[index + 1]['desc']);
SmartDialog.showToast(videoFitType[index + 1]['desc']);
} else {
_videoFit.value = fits[0];
// 默认 contain
_videoFit.value = videoFitType.first['attr'];
SmartDialog.showToast(videoFitType.first['desc']);
}
videoFitChangedTimer = Timer(const Duration(seconds: 1), () {
videoFitChangedTimer = null;
@ -655,9 +664,10 @@ class PlPlayerController {
/// 读取fit
Future<void> getVideoFit() async {
String fitValue = videoStorage.get(VideoBoxKey.videoBrightness,
defaultValue: 'fitHeight');
_videoFit.value = fits.firstWhere((element) => element.name == fitValue);
String fitValue =
videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 'contain');
_videoFit.value = videoFitType
.firstWhere((element) => element['attr'] == fitValue)['attr'];
}
/// 缓存亮度

View File

@ -1,16 +1,36 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
// import 'package:auto_orientation/auto_orientation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
//横屏
/// 低版本xcode不支持auto_orientation
// Future<void> landScape() async {
// if (Platform.isAndroid || Platform.isIOS) {
// await AutoOrientation.landscapeAutoMode(forceSensor: true);
// }
// }
Future<void> landScape() async {
dynamic document;
try {
if (kIsWeb) {
await document.documentElement?.requestFullscreen();
} else if (Platform.isAndroid || Platform.isIOS) {
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersiveSticky,
overlays: [],
);
await SystemChrome.setPreferredOrientations(
[
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
],
);
} else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
await const MethodChannel('com.alexmercerind/media_kit_video')
.invokeMethod(
'Utils.EnterNativeFullscreen',
);
}
} catch (exception, stacktrace) {
debugPrint(exception.toString());
debugPrint(stacktrace.toString());
}
}
//竖屏
Future<void> verticalScreen() async {
@ -27,14 +47,24 @@ Future<void> enterFullScreen() async {
//退出全屏显示
Future<void> exitFullScreen() async {
late SystemUiMode mode;
if ((Platform.isAndroid &&
(await DeviceInfoPlugin().androidInfo).version.sdkInt >= 29) ||
!Platform.isAndroid) {
mode = SystemUiMode.edgeToEdge;
} else {
mode = SystemUiMode.manual;
dynamic document;
try {
if (kIsWeb) {
document.exitFullscreen();
} else if (Platform.isAndroid || Platform.isIOS) {
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
await SystemChrome.setPreferredOrientations([]);
} else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
await const MethodChannel('com.alexmercerind/media_kit_video')
.invokeMethod(
'Utils.ExitNativeFullscreen',
);
}
} catch (exception, stacktrace) {
debugPrint(exception.toString());
debugPrint(stacktrace.toString());
}
await SystemChrome.setEnabledSystemUIMode(mode,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]);
}

View File

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
@ -16,7 +17,6 @@ import 'package:pilipala/plugin/pl_player/utils.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:volume_controller/volume_controller.dart';
import 'utils/fullscreen.dart';
import 'widgets/app_bar_ani.dart';
@ -90,9 +90,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
Future.microtask(() async {
try {
VolumeController().showSystemUI = false;
_volumeValue = await VolumeController().getVolume();
VolumeController().listener((value) {
FlutterVolumeController.showSystemUI = true;
_volumeValue = (await FlutterVolumeController.getVolume())!;
FlutterVolumeController.addListener((value) {
if (mounted && !_volumeInterceptEventStream) {
setState(() {
_volumeValue = value;
@ -118,7 +118,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
Future<void> setVolume(double value) async {
try {
VolumeController().setVolume(value);
FlutterVolumeController.showSystemUI = false;
await FlutterVolumeController.setVolume(value);
} catch (_) {}
setState(() {
_volumeValue = value;
@ -166,7 +167,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 进入全屏
await enterFullScreen();
// 横屏
// await landScape();
await landScape();
} else {
// 竖屏
await verticalScreen();
@ -177,14 +178,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 进入全屏
await enterFullScreen();
// 横屏
// await landScape();
await verticalScreen();
break;
case FullScreenMode.horizontal:
/// 进入全屏
await enterFullScreen();
// 横屏
// await landScape();
await landScape();
break;
}

View File

@ -130,15 +130,15 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
// ),
// ),
// ),
// ComBtn(
// icon: const Icon(
// Icons.fit_screen_sharp,
// size: 18,
// color: Colors.white,
// ),
// fuc: () => _.toggleVideoFit(),
// ),
// const SizedBox(width: 4),
ComBtn(
icon: const Icon(
Icons.settings_overscan_outlined,
size: 18,
color: Colors.white,
),
fuc: () => _.toggleVideoFit(),
),
const SizedBox(width: 4),
// 全屏
Obx(
() => ComBtn(

View File

@ -16,6 +16,7 @@ import 'package:pilipala/pages/member/index.dart';
import 'package:pilipala/pages/preview/index.dart';
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/play_setting.dart';
import 'package:pilipala/pages/setting/privacy_setting.dart';
import 'package:pilipala/pages/setting/style_setting.dart';
@ -80,7 +81,8 @@ class Routes {
GetPage(name: '/styleSetting', page: () => const StyleSetting()),
// 隐私设置
GetPage(name: '/privacySetting', page: () => const PrivacySetting()),
// 其他设置
GetPage(name: '/extraSetting', page: () => const ExtraSetting()),
//
GetPage(name: '/blackListPage', page: () => const BlackListPage()),
// 关于

View File

@ -1,27 +1,22 @@
import 'package:cookie_jar/cookie_jar.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:webview_cookie_manager/webview_cookie_manager.dart';
class SetCookie {
static onSet(List cookiesList, String url) async {
// domain url
List<Cookie> jarCookies = [];
if (cookiesList.isNotEmpty) {
for (var i in cookiesList) {
Cookie jarCookie = Cookie(i.name, i.value);
jarCookies.add(jarCookie);
}
}
String cookiePath = await Utils.getCookiePath();
PersistCookieJar cookieJar = PersistCookieJar(
ignoreExpires: true,
storage: FileStorage(cookiePath),
);
await cookieJar.saveFromResponse(Uri.parse(url), jarCookies);
// 重新设置 cookie
Request.setCookie();
Request.cookieManager.cookieJar
.saveFromResponse(Uri.parse(url), jarCookies);
return true;
static onSet() async {
var cookies = await WebviewCookieManager().getCookies(HttpString.baseUrl);
await Request.cookieManager.cookieJar
.saveFromResponse(Uri.parse(HttpString.baseUrl), cookies);
var cookieString =
cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; ');
Request.dio.options.headers['cookie'] = cookieString;
cookies = await WebviewCookieManager().getCookies(HttpString.baseApiUrl);
await Request.cookieManager.cookieJar
.saveFromResponse(Uri.parse(HttpString.baseApiUrl), cookies);
cookies = await WebviewCookieManager().getCookies(HttpString.tUrl);
await Request.cookieManager.cookieJar
.saveFromResponse(Uri.parse(HttpString.tUrl), cookies);
}
}

39
lib/utils/download.dart Normal file
View File

@ -0,0 +1,39 @@
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadUtils {
// 获取存储全县
static requestStoragePer() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
Permission.photos,
].request();
statuses[Permission.storage].toString();
}
static Future<bool> downloadImg(String imgUrl) async {
await requestStoragePer();
SmartDialog.showLoading(msg: '保存中');
var response = await Dio()
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
String picName =
"plpl_cover_${DateTime.now().toString().split('-').join()}.png";
final result = await ImageGallerySaver.saveImage(
Uint8List.fromList(response.data),
quality: 100,
name: picName,
);
SmartDialog.dismiss();
if (result != null) {
if (result['isSuccess']) {
// ignore: avoid_print
await SmartDialog.showToast('$picName」已保存 ');
}
}
return true;
}
}

53
lib/utils/event_bus.dart Normal file
View File

@ -0,0 +1,53 @@
// 订阅者回调签名
typedef void EventCallback(arg);
class EventBus {
// 私有构造函数
EventBus._internal();
// 保存单例
static final EventBus _singleton = EventBus._internal();
// 工厂构造函数
factory EventBus() => _singleton;
// 保存事件订阅者队列key:事件名(id)value: 对应事件的订阅者队列
final _emap = <dynamic, List<EventCallback>>{};
// 添加订阅者
void on(eventName, EventCallback f) {
_emap[eventName] ??= <EventCallback>[];
_emap[eventName]!.add(f);
}
// 移除订阅者
void off(eventName, [EventCallback? f]) {
var list = _emap[eventName];
if (eventName == null || list == null) return;
if (f == null) {
_emap[eventName] = [];
} else {
list.remove(f);
}
}
// 触发事件,事件触发后该事件所有订阅者会被调用
void emit(eventName, [arg]) {
var list = _emap[eventName];
if (list == null) return;
List<EventCallback> tempList = List.from(list);
for (var callback in tempList) {
callback(arg);
}
}
// 获取订阅者数量
int getSubscriberCount(eventName) {
var list = _emap[eventName];
return list?.length ?? 0;
}
}
class EventName {
static const String loginEvent = 'loginEvent';
}

View File

@ -119,6 +119,7 @@ class SettingBoxKey {
static const String fullScreenMode = 'fullScreenMode';
static const String blackMidsList = 'blackMidsList';
static const String autoUpdate = 'autoUpdate';
}
class LocalCacheKey {

View File

@ -4,8 +4,14 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get_utils/get_utils.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/github/latest.dart';
import 'package:url_launcher/url_launcher.dart';
class Utils {
static Future<String> getCookiePath() async {
@ -194,4 +200,55 @@ class Utils {
}
return false;
}
// 检查更新
static Future<bool> checkUpdata() async {
SmartDialog.dismiss();
var currentInfo = await PackageInfo.fromPlatform();
var result = await Request().get(Api.latestApp);
LatestDataModel data = LatestDataModel.fromJson(result.data);
bool isUpdate = Utils.needUpdate(currentInfo.version, data.tagName!);
if (isUpdate) {
SmartDialog.show(
builder: (context) {
return AlertDialog(
title: const Text('🎉 发现新版本 '),
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data.tagName!,
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 8),
Text(data.body!),
],
),
actions: [
TextButton(
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('去下载')),
],
);
},
);
}
return true;
}
}

View File

@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin.h>
#include <flutter_volume_controller/flutter_volume_controller_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
@ -15,6 +16,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin");
flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);

View File

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

View File

@ -8,6 +8,7 @@ import Foundation
import connectivity_plus
import device_info_plus
import dynamic_color
import flutter_volume_controller
import media_kit_libs_macos_video
import media_kit_video
import package_info_plus
@ -22,6 +23,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))

View File

@ -6,6 +6,8 @@ PODS:
- FlutterMacOS
- dynamic_color (0.0.2):
- FlutterMacOS
- flutter_volume_controller (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
@ -29,6 +31,8 @@ PODS:
- sqflite (0.0.2):
- FlutterMacOS
- FMDB (>= 2.7.5)
- url_launcher_macos (0.0.1):
- FlutterMacOS
- wakelock_plus (0.0.1):
- FlutterMacOS
@ -36,6 +40,7 @@ DEPENDENCIES:
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`)
- flutter_volume_controller (from `Flutter/ephemeral/.symlinks/plugins/flutter_volume_controller/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
- media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`)
@ -45,6 +50,7 @@ DEPENDENCIES:
- screen_brightness_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
SPEC REPOS:
@ -59,6 +65,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
dynamic_color:
:path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos
flutter_volume_controller:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_volume_controller/macos
FlutterMacOS:
:path: Flutter/ephemeral
media_kit_libs_macos_video:
@ -77,6 +85,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
sqflite:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
wakelock_plus:
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
@ -84,6 +94,7 @@ SPEC CHECKSUMS:
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f
flutter_volume_controller: 25d09126b0d695560f11c80b1311d5063fed882f
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
@ -95,6 +106,7 @@ SPEC CHECKSUMS:
screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7

View File

@ -301,10 +301,10 @@ packages:
dependency: "direct main"
description:
name: dio
sha256: "3866d67f93523161b643187af65f5ac08bc991a5bcdaf41a2d587fe4ccb49993"
sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197
url: "https://pub.dev"
source: hosted
version: "5.3.0"
version: "5.3.2"
dio_cookie_manager:
dependency: "direct main"
description:
@ -337,6 +337,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.6.6"
easy_debounce:
dependency: "direct main"
description:
name: easy_debounce
sha256: f082609cfb8f37defb9e37fc28bc978c6712dedf08d4c5a26f820fa10165a236
url: "https://pub.dev"
source: hosted
version: "2.0.3"
extended_image:
dependency: "direct main"
description:
@ -451,6 +459,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
flutter_smart_dialog:
dependency: "direct main"
description:
@ -459,11 +475,27 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.9.3+2"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_volume_controller:
dependency: "direct main"
description:
name: flutter_volume_controller
sha256: "7f88cb046b00fd80e98bcb7926b9e3879f004f30905109fdf6c5d09b8d28eb2e"
url: "https://pub.dev"
source: hosted
version: "1.2.7"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -673,50 +705,50 @@ packages:
dependency: "direct main"
description:
name: media_kit
sha256: f19151ff1a1724ed8675f066b40e74af6d155fc859cb74487daeae2cbeff53e0
sha256: "0a89e7037002a62701ec319c375586849f9ef8e681820e1dd4a4ff7b843f7542"
url: "https://pub.dev"
source: hosted
version: "1.1.3+1"
version: "1.1.4+1"
media_kit_libs_android_video:
dependency: "direct main"
description:
name: media_kit_libs_android_video
sha256: "0a533497d0a982c7146af7dbe226856ef13b05f6d87a6405b1d09d8b40aa2685"
sha256: "142d389bf3efcf8469594a9c7a06a92fc25843fc6c0c3247f76cdcf70b3b29de"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
media_kit_libs_ios_video:
dependency: "direct main"
description:
name: media_kit_libs_ios_video
sha256: "781bcf867663a8b60734eab0f5cc785ea7651cb2504acd84055a844d81075ea9"
sha256: fed403dc9d54462e51ee80e0cb23c12a53fadea9a8fa18aca2de9054176d1159
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.1.3"
media_kit_libs_linux:
dependency: "direct main"
description:
name: media_kit_libs_linux
sha256: "838b9e8041d376873cc938872c75812989d0feb247ad93afd8dbc92bf052680a"
sha256: "570bf18ebbd1221caec082657468be05d180510385d3515ec38e0be44fdcc859"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
media_kit_libs_macos_video:
dependency: "direct main"
description:
name: media_kit_libs_macos_video
sha256: "6bdf6e2a4ac88af5385b4ca20d0288439a76364fa42e5cf80e2cc63c3b358298"
sha256: c06e831f3c22a45296d375788d9bc07871b448f8e9ec98d77b11e5e118a83fb2
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.1.3"
media_kit_libs_windows_video:
dependency: "direct main"
description:
name: media_kit_libs_windows_video
sha256: "31c6c75ab08627df9fce7ce2431e5ac5e5990ac40faef75b07c48d1d7907a37f"
sha256: f33aabd8414470d99e2c91dd98d605e6a5f1c4b8082dd933c10951bc961b9124
url: "https://pub.dev"
source: hosted
version: "1.0.6"
version: "1.0.7"
media_kit_native_event_loop:
dependency: "direct main"
description:
@ -729,10 +761,10 @@ packages:
dependency: "direct main"
description:
name: media_kit_video
sha256: e286992beee857fee78ce79ed21fd38addb5af0469e10a322d82cf074beb6214
sha256: e7fcbe426d42a78ad6696f8f557adb9cbdc012177829026d04992cc106a1c815
url: "https://pub.dev"
source: hosted
version: "1.1.3"
version: "1.1.5"
meta:
dependency: transitive
description:
@ -797,6 +829,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.3"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_provider:
dependency: "direct main"
description:
@ -1258,6 +1298,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.7"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f"
url: "https://pub.dev"
source: hosted
version: "1.1.7"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f"
url: "https://pub.dev"
source: hosted
version: "1.1.7"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e"
url: "https://pub.dev"
source: hosted
version: "1.1.7"
vector_math:
dependency: transitive
description:
@ -1275,7 +1339,7 @@ packages:
source: hosted
version: "0.4.0+2"
volume_controller:
dependency: "direct main"
dependency: transitive
description:
name: volume_controller
sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9"

View File

@ -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.0+1
version: 1.0.2
environment:
sdk: ">=2.19.6 <3.0.0"
@ -91,28 +91,30 @@ dependencies:
crypto: ^3.0.3
# 视频播放器
media_kit: ^1.1.3 # Primary package.
media_kit_video: ^1.1.3 # For video rendering.
media_kit: ^1.1.4 # Primary package.
media_kit_video: ^1.1.5 # For video rendering.
media_kit_native_event_loop: ^1.0.7 # Support for higher number of concurrent instances & better performance.
media_kit_libs_android_video: ^1.3.1 # Android package for video native libraries.
media_kit_libs_ios_video: ^1.1.2 # iOS package for video native libraries.
media_kit_libs_macos_video: ^1.1.2 # macOS package for video native libraries.
media_kit_libs_windows_video: ^1.0.6 # Windows package for video native libraries.
media_kit_libs_linux: ^1.1.0
media_kit_libs_android_video: ^1.3.2 # Android package for video native libraries.
media_kit_libs_ios_video: ^1.1.3 # iOS package for video native libraries.
media_kit_libs_macos_video: ^1.1.3 # macOS package for video native libraries.
media_kit_libs_windows_video: ^1.0.7 # Windows package for video native libraries.
media_kit_libs_linux: ^1.1.1
# 音量、亮度、屏幕控制
volume_controller: ^2.0.7
flutter_volume_controller: ^1.2.7
screen_brightness: ^0.2.2
wakelock_plus: ^1.1.1
universal_platform: ^1.0.0+1
# 进度条
audio_video_progress_bar: ^1.0.1
animations: ^2.0.7
# auto_orientation: ^2.3.1
# 获取appx信息
package_info_plus: ^4.1.0
url_launcher: ^6.1.12
flutter_svg: ^2.0.7
# 防抖节流
easy_debounce: ^2.0.3
dev_dependencies:
flutter_test:

View File

@ -8,6 +8,7 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
@ -20,6 +21,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
DynamicColorPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FlutterVolumeControllerPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterVolumeControllerPluginCApi"));
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar(

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
dynamic_color
flutter_volume_controller
media_kit_libs_windows_video
media_kit_video
permission_handler_windows