Merge branch 'main' into feature-playerSubtitle
This commit is contained in:
@ -45,10 +45,13 @@ class ApiInterceptor extends Interceptor {
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
// 处理网络请求错误
|
||||
// handler.next(err);
|
||||
SmartDialog.showToast(
|
||||
await dioError(err),
|
||||
displayType: SmartToastType.onlyRefresh,
|
||||
);
|
||||
String url = err.requestOptions.uri.toString();
|
||||
if (!url.contains('heartBeat')) {
|
||||
SmartDialog.showToast(
|
||||
await dioError(err),
|
||||
displayType: SmartToastType.onlyRefresh,
|
||||
);
|
||||
}
|
||||
super.onError(err, handler);
|
||||
}
|
||||
|
||||
@ -75,23 +78,24 @@ class ApiInterceptor extends Interceptor {
|
||||
}
|
||||
|
||||
static Future<String> checkConnect() async {
|
||||
final ConnectivityResult connectivityResult =
|
||||
final List<ConnectivityResult> connectivityResult =
|
||||
await Connectivity().checkConnectivity();
|
||||
switch (connectivityResult) {
|
||||
case ConnectivityResult.mobile:
|
||||
return '正在使用移动流量';
|
||||
case ConnectivityResult.wifi:
|
||||
return '正在使用wifi';
|
||||
case ConnectivityResult.ethernet:
|
||||
return '正在使用局域网';
|
||||
case ConnectivityResult.vpn:
|
||||
return '正在使用代理网络';
|
||||
case ConnectivityResult.other:
|
||||
return '正在使用其他网络';
|
||||
case ConnectivityResult.none:
|
||||
return '未连接到任何网络';
|
||||
default:
|
||||
return '';
|
||||
if (connectivityResult.contains(ConnectivityResult.mobile)) {
|
||||
return '正在使用移动流量';
|
||||
} else if (connectivityResult.contains(ConnectivityResult.wifi)) {
|
||||
return '正在使用wifi';
|
||||
} else if (connectivityResult.contains(ConnectivityResult.ethernet)) {
|
||||
return '正在使用局域网';
|
||||
} else if (connectivityResult.contains(ConnectivityResult.vpn)) {
|
||||
return '正在使用代理网络';
|
||||
} else if (connectivityResult.contains(ConnectivityResult.bluetooth)) {
|
||||
return '正在使用蓝牙网络';
|
||||
} else if (connectivityResult.contains(ConnectivityResult.other)) {
|
||||
return '正在使用其他网络';
|
||||
} else if (connectivityResult.contains(ConnectivityResult.none)) {
|
||||
return '未连接到任何网络';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,9 +148,9 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
||||
Expanded(
|
||||
child: Material(
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: widget.pages.length,
|
||||
itemCount: widget.pages.length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
bool isLastItem = index == widget.pages.length - 1;
|
||||
bool isLastItem = index == widget.pages.length;
|
||||
bool isCurrentIndex = currentIndex == index;
|
||||
return isLastItem
|
||||
? SizedBox(
|
||||
|
@ -88,8 +88,10 @@ class HistoryController extends GetxController {
|
||||
// 观看历史暂停状态
|
||||
Future historyStatus() async {
|
||||
var res = await UserHttp.historyStatus();
|
||||
pauseStatus.value = res.data['data'];
|
||||
localCache.put(LocalCacheKey.historyPause, res.data['data']);
|
||||
if (res.data['code'] == 0) {
|
||||
pauseStatus.value = res.data['data'];
|
||||
localCache.put(LocalCacheKey.historyPause, res.data['data']);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空观看历史
|
||||
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/user/fav_folder.dart';
|
||||
import 'package:pilipala/pages/main/index.dart';
|
||||
@ -102,7 +103,11 @@ class _MediaPageState extends State<MediaPage>
|
||||
],
|
||||
Obx(() => mediaController.userLogin.value
|
||||
? favFolder(mediaController, context)
|
||||
: const SizedBox())
|
||||
: const SizedBox()),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).padding.bottom +
|
||||
kBottomNavigationBarHeight,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -9,7 +9,7 @@ import 'package:pilipala/utils/storage.dart';
|
||||
class RankController extends GetxController with GetTickerProviderStateMixin {
|
||||
bool flag = false;
|
||||
late RxList tabs = [].obs;
|
||||
RxInt initialIndex = 1.obs;
|
||||
RxInt initialIndex = 0.obs;
|
||||
late TabController tabController;
|
||||
late List tabsCtrList;
|
||||
late List<Widget> tabsPageList;
|
||||
|
@ -22,15 +22,20 @@ class ZonePage extends StatefulWidget {
|
||||
State<ZonePage> createState() => _ZonePageState();
|
||||
}
|
||||
|
||||
class _ZonePageState extends State<ZonePage> {
|
||||
final ZoneController _zoneController = Get.put(ZoneController());
|
||||
class _ZonePageState extends State<ZonePage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late ZoneController _zoneController;
|
||||
List videoList = [];
|
||||
Future? _futureBuilderFuture;
|
||||
late ScrollController scrollController;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_zoneController = Get.put(ZoneController(), tag: widget.rid.toString());
|
||||
_futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid);
|
||||
scrollController = _zoneController.scrollController;
|
||||
StreamController<bool> mainStream =
|
||||
@ -68,6 +73,7 @@ class _ZonePageState extends State<ZonePage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
return await _zoneController.onRefresh();
|
||||
|
@ -85,10 +85,9 @@ class VideoIntroController extends GetxController {
|
||||
if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) {
|
||||
lastPlayCid.value = videoDetail.value.pages!.first.cid!;
|
||||
}
|
||||
// Get.find<VideoDetailController>(tag: heroTag).tabs.value = [
|
||||
// '简介',
|
||||
// '评论 ${result['data']!.stat!.reply}'
|
||||
// ];
|
||||
final VideoDetailController videoDetailCtr =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}'];
|
||||
// 获取到粉丝数再返回
|
||||
await queryUserStat();
|
||||
}
|
||||
|
@ -161,9 +161,9 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
Expanded(
|
||||
child: Material(
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: episodes.length,
|
||||
itemCount: episodes.length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
bool isLastItem = index == episodes.length - 1;
|
||||
bool isLastItem = index == episodes.length;
|
||||
bool isCurrentIndex = currentIndex == index;
|
||||
return isLastItem
|
||||
? SizedBox(
|
||||
|
@ -148,35 +148,14 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
floating: true,
|
||||
delegate: _MySliverPersistentHeaderDelegate(
|
||||
child: Container(
|
||||
height: 45,
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
.withOpacity(0.1)),
|
||||
),
|
||||
),
|
||||
height: 40,
|
||||
padding: const EdgeInsets.fromLTRB(12, 6, 6, 0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Obx(
|
||||
() => AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
transitionBuilder:
|
||||
(Widget child, Animation<double> animation) {
|
||||
return ScaleTransition(
|
||||
scale: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
'共${_videoReplyController.count.value}条回复',
|
||||
key: ValueKey<int>(
|
||||
_videoReplyController.count.value),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${_videoReplyController.sortTypeLabel.value}评论',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
@ -184,10 +163,12 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
onPressed: () =>
|
||||
_videoReplyController.queryBySort(),
|
||||
icon: const Icon(Icons.sort, size: 16),
|
||||
label: Obx(() => Text(
|
||||
_videoReplyController.sortTypeLabel.value,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
)),
|
||||
label: Obx(
|
||||
() => Text(
|
||||
_videoReplyController.sortTypeLabel.value,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
@ -329,8 +310,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
|
||||
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
_MySliverPersistentHeaderDelegate({required this.child});
|
||||
final double _minExtent = 45;
|
||||
final double _maxExtent = 45;
|
||||
final double _minExtent = 40;
|
||||
final double _maxExtent = 40;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
|
@ -498,7 +498,7 @@ InlineSpan buildContent(
|
||||
return str;
|
||||
});
|
||||
}
|
||||
// content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' ');
|
||||
content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' ');
|
||||
content.message = content.message
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
|
@ -5,6 +5,7 @@ import 'dart:ui';
|
||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||
import 'package:floating/floating.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -322,62 +323,74 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: TabBar(
|
||||
controller: vdCtr.tabCtr,
|
||||
dividerColor: Colors.transparent,
|
||||
tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 220,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32,
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () => vdCtr.showShootDanmakuSheet(),
|
||||
child: const Text('发弹幕', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
SizedBox(
|
||||
width: 34,
|
||||
height: 32,
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
plPlayerController?.isOpenDanmu.value =
|
||||
!(plPlayerController?.isOpenDanmu.value ?? false);
|
||||
},
|
||||
child: Obx(() => Text(
|
||||
'弹',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: (plPlayerController?.isOpenDanmu.value ??
|
||||
false)
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
],
|
||||
child: Material(
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Obx(
|
||||
() => TabBar(
|
||||
padding: EdgeInsets.zero,
|
||||
controller: vdCtr.tabCtr,
|
||||
labelStyle: const TextStyle(fontSize: 13),
|
||||
labelPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度
|
||||
dividerColor: Colors.transparent,
|
||||
tabs: vdCtr.tabs
|
||||
.map(
|
||||
(String name) => Tab(text: name),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32,
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () => vdCtr.showShootDanmakuSheet(),
|
||||
child:
|
||||
const Text('发弹幕', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 38,
|
||||
height: 38,
|
||||
child: Obx(
|
||||
() => IconButton(
|
||||
onPressed: () {
|
||||
plPlayerController?.isOpenDanmu.value =
|
||||
!(plPlayerController?.isOpenDanmu.value ??
|
||||
false);
|
||||
},
|
||||
icon: !(plPlayerController?.isOpenDanmu.value ??
|
||||
false)
|
||||
? SvgPicture.asset(
|
||||
'assets/images/video/danmu_close.svg',
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
'assets/images/video/danmu_open.svg',
|
||||
// ignore: deprecated_member_use
|
||||
color:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -7,4 +7,5 @@ enum BottomControlType {
|
||||
fit,
|
||||
speed,
|
||||
fullscreen,
|
||||
custom,
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@ -34,6 +35,8 @@ class PLVideoPlayer extends StatefulWidget {
|
||||
this.bottomControl,
|
||||
this.danmuWidget,
|
||||
this.bottomList,
|
||||
this.customWidget,
|
||||
this.customWidgets,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@ -42,6 +45,10 @@ class PLVideoPlayer extends StatefulWidget {
|
||||
final PreferredSizeWidget? bottomControl;
|
||||
final Widget? danmuWidget;
|
||||
final List<BottomControlType>? bottomList;
|
||||
// List<Widget> or Widget
|
||||
|
||||
final Widget? customWidget;
|
||||
final List<Widget>? customWidgets;
|
||||
|
||||
@override
|
||||
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
||||
@ -310,7 +317,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
),
|
||||
};
|
||||
final List<Widget> list = [];
|
||||
var userSpecifyItem = widget.bottomList ??
|
||||
List<BottomControlType> userSpecifyItem = widget.bottomList ??
|
||||
[
|
||||
BottomControlType.playOrPause,
|
||||
BottomControlType.time,
|
||||
@ -319,7 +326,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
BottomControlType.fullscreen,
|
||||
];
|
||||
for (var i = 0; i < userSpecifyItem.length; i++) {
|
||||
list.add(videoProgressWidgets[userSpecifyItem[i]]!);
|
||||
if (userSpecifyItem[i] == BottomControlType.custom) {
|
||||
if (widget.customWidget != null && widget.customWidget is Widget) {
|
||||
list.add(widget.customWidget!);
|
||||
}
|
||||
if (widget.customWidgets != null && widget.customWidgets!.isNotEmpty) {
|
||||
list.addAll(widget.customWidgets!);
|
||||
}
|
||||
} else {
|
||||
list.add(videoProgressWidgets[userSpecifyItem[i]]!);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@ -346,6 +362,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
children: <Widget>[
|
||||
Obx(
|
||||
() => Video(
|
||||
key: ValueKey(_.videoFit.value),
|
||||
controller: videoController,
|
||||
controls: NoVideoControls,
|
||||
pauseUponEnteringBackgroundMode: !enableBackgroundPlay,
|
||||
@ -710,13 +727,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
_distance.value = dy;
|
||||
} else {
|
||||
// 右边区域 👈
|
||||
final double level = (_.isFullScreen.value
|
||||
? Get.size.height
|
||||
: screenWidth * 9 / 16) *
|
||||
3;
|
||||
final double volume = _volumeValue.value - delta / level;
|
||||
final double result = volume.clamp(0.0, 1.0);
|
||||
setVolume(result);
|
||||
EasyThrottle.throttle(
|
||||
'setVolume', const Duration(milliseconds: 20), () {
|
||||
final double level = (_.isFullScreen.value
|
||||
? Get.size.height
|
||||
: screenWidth * 9 / 16);
|
||||
final double volume = _volumeValue.value -
|
||||
double.parse(delta.toStringAsFixed(1)) / level;
|
||||
final double result = volume.clamp(0.0, 1.0);
|
||||
setVolume(result);
|
||||
});
|
||||
}
|
||||
},
|
||||
onVerticalDragEnd: (DragEndDetails details) {},
|
||||
|
Reference in New Issue
Block a user