Compare commits
24 Commits
v1.0.0.081
...
v1.0.2.081
Author | SHA1 | Date | |
---|---|---|---|
599d6983fc | |||
b7eed8578a | |||
ec9d9739fe | |||
740116e873 | |||
2b0dc9d285 | |||
7c2518bcd2 | |||
9db76e99db | |||
40849cb68d | |||
b435023c99 | |||
b55568ef2a | |||
47a3c964c0 | |||
85ff7c7a92 | |||
083d05ed7e | |||
08862e7f72 | |||
04d953a3a2 | |||
be1cc25d12 | |||
adff2f2828 | |||
592b32fc7a | |||
d9a758464c | |||
0c067f7ca6 | |||
b6aa144205 | |||
438a94e298 | |||
b800f1b83b | |||
ddceda1184 |
23
.github/ISSUE_TEMPLATE/bug-反馈.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/bug-反馈.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Bug 反馈
|
||||
about: 描述你所遇到的bug
|
||||
title: ''
|
||||
labels: 问题反馈
|
||||
assignees: guozhigq
|
||||
|
||||
---
|
||||
|
||||
### 问题描述
|
||||
请提供一个清晰而简明的问题描述。
|
||||
|
||||
### 复现步骤
|
||||
请提供复现该问题所需的具体步骤。
|
||||
|
||||
### 预期行为
|
||||
请描述你期望的正确行为或结果。
|
||||
|
||||
### 系统信息
|
||||
请提供关于您的环境的详细信息,包括操作系统、浏览器版本等。
|
||||
|
||||
### 相关截图或日志
|
||||
如果有的话,请提供相关的截图、错误日志或其他有助于解决问题的信息。
|
20
.github/ISSUE_TEMPLATE/功能请求.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/功能请求.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: 功能请求
|
||||
about: 对于功能的一些建议
|
||||
title: ''
|
||||
labels: 功能
|
||||
assignees: guozhigq
|
||||
|
||||
---
|
||||
|
||||
### 功能描述
|
||||
请提供对所请求功能的清晰描述。
|
||||
|
||||
### 目标
|
||||
请描述你希望通过这个功能实现的目标。
|
||||
|
||||
### 解决方案
|
||||
如果你有任何关于如何实现这个功能的想法或建议,请在这里提供。
|
||||
|
||||
### 其他
|
||||
请提供已实现该功能或类似功能的应用
|
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@ -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
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
assets/images/error.svg
Normal file
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
7
change_log/1.0.1.0817.md
Normal file
@ -0,0 +1,7 @@
|
||||
## 1.0.1
|
||||
|
||||
### 修复
|
||||
+ 升级播放器依赖
|
||||
+ android平台 AV1格式视频支持
|
||||
+ 视频全屏功能
|
||||
|
19
change_log/1.0.2.0819.md
Normal file
19
change_log/1.0.2.0819.md
Normal file
@ -0,0 +1,19 @@
|
||||
## 1.0.2
|
||||
|
||||
### 新功能
|
||||
+ 自动检查更新
|
||||
+ 封面图片保存
|
||||
+ 动态跳转番剧
|
||||
+ 历史记录番剧记忆播放
|
||||
+ 一键清空稍后再看
|
||||
|
||||
### 修复
|
||||
+ 切换分P cid未切换
|
||||
+ cookie存储问题
|
||||
+ 登录/退出登录问题
|
||||
|
||||
### 优化
|
||||
+ 页面空/异常状态样式
|
||||
+ 退出登录提示
|
||||
+ 请求节流
|
||||
+ 全屏播放
|
@ -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
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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 ?? '点击重试'),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -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(
|
||||
|
31
lib/common/widgets/no_data.dart
Normal file
31
lib/common/widgets/no_data.dart
Normal 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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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),
|
||||
)
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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(
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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']};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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 =
|
||||
|
@ -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,
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ]
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -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'];
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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))
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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('确认'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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}',
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
@ -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: {
|
||||
|
@ -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']) {
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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('确认'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 开启关闭震动反馈
|
||||
|
37
lib/pages/setting/extra_setting.dart
Normal file
37
lib/pages/setting/extra_setting.dart
Normal 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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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(() {});
|
||||
}),
|
||||
),
|
||||
|
@ -414,6 +414,7 @@ class VideoIntroController extends GetxController {
|
||||
videoReplyCtr.queryReplyList(type: 'init');
|
||||
} catch (_) {}
|
||||
this.bvid = bvid;
|
||||
lastPlayCid.value = cid;
|
||||
await queryVideoIntro();
|
||||
}
|
||||
}
|
||||
|
@ -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 &&
|
||||
|
@ -38,6 +38,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
item.cid,
|
||||
item.aid,
|
||||
);
|
||||
currentIndex = i;
|
||||
Get.back();
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 =
|
||||
|
@ -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) ...[
|
||||
|
@ -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);
|
||||
|
@ -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'];
|
||||
}
|
||||
|
||||
/// 缓存亮度
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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()),
|
||||
// 关于
|
||||
|
@ -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
39
lib/utils/download.dart
Normal 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
53
lib/utils/event_bus.dart
Normal 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';
|
||||
}
|
@ -119,6 +119,7 @@ class SettingBoxKey {
|
||||
static const String fullScreenMode = 'fullScreenMode';
|
||||
|
||||
static const String blackMidsList = 'blackMidsList';
|
||||
static const String autoUpdate = 'autoUpdate';
|
||||
}
|
||||
|
||||
class LocalCacheKey {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
flutter_volume_controller
|
||||
media_kit_libs_linux
|
||||
media_kit_video
|
||||
url_launcher_linux
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
98
pubspec.lock
98
pubspec.lock
@ -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"
|
||||
|
22
pubspec.yaml
22
pubspec.yaml
@ -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:
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user