mod: format code
This commit is contained in:
@ -12,7 +12,7 @@ import 'package:pilipala/models/common/search_type.dart';
|
||||
import 'package:pilipala/models/video/play/quality.dart';
|
||||
import 'package:pilipala/models/video/play/url.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/reply_reply/index.dart';
|
||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
@ -92,7 +92,7 @@ class VideoDetailController extends GetxController
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
Map argMap = Get.arguments;
|
||||
final Map argMap = Get.arguments;
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
var keys = argMap.keys.toList();
|
||||
if (keys.isNotEmpty) {
|
||||
@ -188,8 +188,8 @@ class VideoDetailController extends GetxController
|
||||
|
||||
/// 根据currentAudioQa 重新设置audioUrl
|
||||
if (currentAudioQa != null) {
|
||||
AudioItem firstAudio = data.dash!.audio!.firstWhere(
|
||||
(i) => i.id == currentAudioQa!.code,
|
||||
final AudioItem firstAudio = data.dash!.audio!.firstWhere(
|
||||
(AudioItem i) => i.id == currentAudioQa!.code,
|
||||
orElse: () => data.dash!.audio!.first,
|
||||
);
|
||||
audioUrl = firstAudio.baseUrl ?? '';
|
||||
@ -246,7 +246,7 @@ class VideoDetailController extends GetxController
|
||||
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||
if (result['status']) {
|
||||
data = result['data'];
|
||||
List<VideoItem> allVideosList = data.dash!.video!;
|
||||
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||
try {
|
||||
// 当前可播放的最高质量视频
|
||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||
@ -255,7 +255,7 @@ class VideoDetailController extends GetxController
|
||||
int resVideoQa = currentHighVideoQa;
|
||||
if (cacheVideoQa! <= currentHighVideoQa) {
|
||||
// 如果预设的画质低于当前最高
|
||||
List<int> numbers = data.acceptQuality!
|
||||
final List<int> numbers = data.acceptQuality!
|
||||
.where((e) => e <= currentHighVideoQa)
|
||||
.toList();
|
||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
||||
@ -263,13 +263,13 @@ class VideoDetailController extends GetxController
|
||||
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
||||
|
||||
/// 取出符合当前画质的videoList
|
||||
List<VideoItem> videosList =
|
||||
final List<VideoItem> videosList =
|
||||
allVideosList.where((e) => e.quality!.code == resVideoQa).toList();
|
||||
|
||||
/// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式
|
||||
List<FormatItem> supportFormats = data.supportFormats!;
|
||||
final List<FormatItem> supportFormats = data.supportFormats!;
|
||||
// 根据画质选编码格式
|
||||
List supportDecodeFormats =
|
||||
final List supportDecodeFormats =
|
||||
supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!;
|
||||
// 默认从设置中取AVC
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
||||
@ -304,7 +304,7 @@ class VideoDetailController extends GetxController
|
||||
|
||||
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
||||
late AudioItem? firstAudio;
|
||||
List<AudioItem> audiosList = data.dash!.audio!;
|
||||
final List<AudioItem> audiosList = data.dash!.audio!;
|
||||
|
||||
try {
|
||||
if (data.dash!.dolby?.audio?.isNotEmpty == true) {
|
||||
@ -318,7 +318,7 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
|
||||
if (audiosList.isNotEmpty) {
|
||||
List<int> numbers = audiosList.map((map) => map.id!).toList();
|
||||
final List<int> numbers = audiosList.map((map) => map.id!).toList();
|
||||
int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
|
||||
if (!numbers.contains(cacheAudioQa) &&
|
||||
numbers.any((e) => e > cacheAudioQa)) {
|
||||
|
||||
@ -389,7 +389,7 @@ class VideoIntroController extends GetxController {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
int currentStatus = followStatus['attribute'];
|
||||
final int currentStatus = followStatus['attribute'];
|
||||
int actionStatus = 0;
|
||||
switch (currentStatus) {
|
||||
case 0:
|
||||
@ -467,7 +467,7 @@ class VideoIntroController extends GetxController {
|
||||
// 修改分P或番剧分集
|
||||
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
||||
// 重新获取视频资源
|
||||
VideoDetailController videoDetailCtr =
|
||||
final VideoDetailController videoDetailCtr =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
videoDetailCtr.bvid = bvid;
|
||||
videoDetailCtr.cid.value = cid;
|
||||
@ -476,7 +476,7 @@ class VideoIntroController extends GetxController {
|
||||
// 重新请求评论
|
||||
try {
|
||||
/// 未渲染回复组件时可能异常
|
||||
VideoReplyController videoReplyCtr =
|
||||
final VideoReplyController videoReplyCtr =
|
||||
Get.find<VideoReplyController>(tag: heroTag);
|
||||
videoReplyCtr.aid = aid;
|
||||
videoReplyCtr.queryReplyList(type: 'init');
|
||||
@ -517,29 +517,27 @@ class VideoIntroController extends GetxController {
|
||||
|
||||
/// 列表循环或者顺序播放时,自动播放下一个
|
||||
void nextPlay() {
|
||||
late List episodes;
|
||||
final List episodes = [];
|
||||
bool isPages = false;
|
||||
if (videoDetail.value.ugcSeason != null) {
|
||||
UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||
List<SectionItem> sections = ugcSeason.sections!;
|
||||
episodes = [];
|
||||
|
||||
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||
final List<SectionItem> sections = ugcSeason.sections!;
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||
episodes.addAll(episodesList);
|
||||
}
|
||||
} else if (videoDetail.value.pages != null) {
|
||||
isPages = true;
|
||||
List<Part> pages = videoDetail.value.pages!;
|
||||
episodes = [];
|
||||
final List<Part> pages = videoDetail.value.pages!;
|
||||
episodes.addAll(pages);
|
||||
}
|
||||
|
||||
int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
||||
final int currentIndex =
|
||||
episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
||||
int nextIndex = currentIndex + 1;
|
||||
VideoDetailController videoDetailCtr =
|
||||
final VideoDetailController videoDetailCtr =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||
|
||||
// 列表循环
|
||||
if (nextIndex >= episodes.length) {
|
||||
@ -550,9 +548,9 @@ class VideoIntroController extends GetxController {
|
||||
return;
|
||||
}
|
||||
}
|
||||
int cid = episodes[nextIndex].cid!;
|
||||
String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
||||
int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
||||
final int cid = episodes[nextIndex].cid!;
|
||||
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
||||
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
||||
changeSeasonOrbangu(rBvid, cid, rAid);
|
||||
}
|
||||
|
||||
@ -567,7 +565,7 @@ class VideoIntroController extends GetxController {
|
||||
// ai总结
|
||||
Future aiConclusion() async {
|
||||
SmartDialog.showLoading(msg: '正在生产ai总结');
|
||||
var res = await VideoHttp.aiConclusion(
|
||||
final res = await VideoHttp.aiConclusion(
|
||||
bvid: bvid,
|
||||
cid: lastPlayCid.value,
|
||||
upMid: videoDetail.value.owner!.mid!,
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
@ -26,7 +24,7 @@ import 'widgets/page.dart';
|
||||
import 'widgets/season.dart';
|
||||
|
||||
class VideoIntroPanel extends StatefulWidget {
|
||||
const VideoIntroPanel({Key? key}) : super(key: key);
|
||||
const VideoIntroPanel({super.key});
|
||||
|
||||
@override
|
||||
State<VideoIntroPanel> createState() => _VideoIntroPanelState();
|
||||
@ -124,8 +122,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
late final VideoDetailController videoDetailCtr;
|
||||
late final Map<dynamic, dynamic> videoItem;
|
||||
|
||||
Box localCache = GStrorage.localCache;
|
||||
Box setting = GStrorage.setting;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
final Box<dynamic> setting = GStrorage.setting;
|
||||
late double sheetHeight;
|
||||
|
||||
late final bool loadingStatus; // 加载状态
|
||||
@ -138,12 +136,15 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
late bool enableAi;
|
||||
bool isProcessing = false;
|
||||
void Function()? handleState(Future Function() action) {
|
||||
return isProcessing ? null : () async {
|
||||
setState(() => isProcessing = true);
|
||||
await action();
|
||||
setState(() => isProcessing = false);
|
||||
};
|
||||
return isProcessing
|
||||
? null
|
||||
: () async {
|
||||
setState(() => isProcessing = true);
|
||||
await action();
|
||||
setState(() => isProcessing = false);
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -168,7 +169,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
bool enableDragQuickFav =
|
||||
final bool enableDragQuickFav =
|
||||
setting.get(SettingBoxKey.enableQuickFav, defaultValue: false);
|
||||
// 快速收藏 &
|
||||
// 点按 收藏至默认文件夹
|
||||
@ -182,7 +183,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
return FavPanel(ctr: videoIntroController);
|
||||
},
|
||||
);
|
||||
@ -192,7 +193,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
return FavPanel(ctr: videoIntroController);
|
||||
},
|
||||
);
|
||||
@ -202,7 +203,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
return FavPanel(ctr: videoIntroController);
|
||||
},
|
||||
);
|
||||
@ -251,8 +252,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ThemeData t = Theme.of(context);
|
||||
Color outline = t.colorScheme.outline;
|
||||
final ThemeData t = Theme.of(context);
|
||||
final Color outline = t.colorScheme.outline;
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10),
|
||||
@ -333,7 +334,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
top: 6,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
var res =
|
||||
final res =
|
||||
await videoIntroController.aiConclusion();
|
||||
if (res['status']) {
|
||||
showAiBottomSheet();
|
||||
@ -472,13 +473,14 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget actionGrid(BuildContext context, videoIntroController) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 6, bottom: 4),
|
||||
height: constraints.maxWidth / 5 * 0.8,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
crossAxisCount: 5,
|
||||
childAspectRatio: 1.25,
|
||||
children: <Widget>[
|
||||
@ -543,7 +545,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
|
||||
return Row(children: [
|
||||
return Row(children: <Widget>[
|
||||
Obx(
|
||||
() => ActionRowItem(
|
||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||
|
||||
@ -6,15 +6,15 @@ import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class FavPanel extends StatefulWidget {
|
||||
final dynamic ctr;
|
||||
const FavPanel({super.key, this.ctr});
|
||||
final dynamic ctr;
|
||||
|
||||
@override
|
||||
State<FavPanel> createState() => _FavPanelState();
|
||||
}
|
||||
|
||||
class _FavPanelState extends State<FavPanel> {
|
||||
Box localCache = GStrorage.localCache;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
late Future _futureBuilderFuture;
|
||||
|
||||
@ -45,7 +45,7 @@ class _FavPanelState extends State<FavPanel> {
|
||||
child: Material(
|
||||
child: FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['status']) {
|
||||
@ -109,7 +109,7 @@ class _FavPanelState extends State<FavPanel> {
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
children: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
style: TextButton.styleFrom(
|
||||
|
||||
@ -17,7 +17,7 @@ class GroupPanel extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupPanelState extends State<GroupPanel> {
|
||||
Box localCache = GStrorage.localCache;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
late Future _futureBuilderFuture;
|
||||
late List<MemberTagItemModel> tagsList;
|
||||
@ -33,17 +33,20 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
void onSave() async {
|
||||
feedBack();
|
||||
// 是否有选中的 有选中的带id,没选使用默认0
|
||||
bool anyHasChecked = tagsList.any((e) => e.checked == true);
|
||||
final bool anyHasChecked =
|
||||
tagsList.any((MemberTagItemModel e) => e.checked == true);
|
||||
late String tagids;
|
||||
if (anyHasChecked) {
|
||||
List checkedList = tagsList.where((e) => e.checked == true).toList();
|
||||
List<int> tagidList = checkedList.map<int>((e) => e.tagid).toList();
|
||||
final List<MemberTagItemModel> checkedList =
|
||||
tagsList.where((MemberTagItemModel e) => e.checked == true).toList();
|
||||
final List<int> tagidList =
|
||||
checkedList.map<int>((e) => e.tagid!).toList();
|
||||
tagids = tagidList.join(',');
|
||||
} else {
|
||||
tagids = '0';
|
||||
}
|
||||
// 保存
|
||||
var res = await MemberHttp.addUsers(widget.mid, tagids);
|
||||
final res = await MemberHttp.addUsers(widget.mid, tagids);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
if (res['status']) {
|
||||
Get.back();
|
||||
@ -56,7 +59,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
height: sheetHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
AppBar(
|
||||
centerTitle: false,
|
||||
elevation: 0,
|
||||
@ -70,7 +73,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
child: Material(
|
||||
child: FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['status']) {
|
||||
|
||||
@ -12,12 +12,11 @@ Box localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
|
||||
class IntroDetail extends StatelessWidget {
|
||||
final dynamic videoDetail;
|
||||
|
||||
const IntroDetail({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.videoDetail,
|
||||
}) : super(key: key);
|
||||
});
|
||||
final dynamic videoDetail;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -86,13 +85,11 @@ class IntroDetail extends StatelessWidget {
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: SelectableRegion(
|
||||
magnifierConfiguration:
|
||||
const TextMagnifierConfiguration(),
|
||||
focusNode: FocusNode(),
|
||||
selectionControls: MaterialTextSelectionControls(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
children: <Widget>[
|
||||
Text(
|
||||
videoDetail!.bvid!,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
@ -122,20 +119,21 @@ class IntroDetail extends StatelessWidget {
|
||||
}
|
||||
|
||||
InlineSpan buildContent(BuildContext context, content) {
|
||||
List descV2 = content.descV2;
|
||||
final List descV2 = content.descV2;
|
||||
// type
|
||||
// 1 普通文本
|
||||
// 2 @用户
|
||||
List<TextSpan> spanChilds = List.generate(descV2.length, (index) {
|
||||
final List<TextSpan> spanChilds = List.generate(descV2.length, (index) {
|
||||
final currentDesc = descV2[index];
|
||||
switch (currentDesc.type) {
|
||||
case 1:
|
||||
List<InlineSpan> spanChildren = [];
|
||||
RegExp urlRegExp = RegExp(r'https?://\S+\b');
|
||||
Iterable<Match> matches = urlRegExp.allMatches(currentDesc.rawText);
|
||||
final List<InlineSpan> spanChildren = <InlineSpan>[];
|
||||
final RegExp urlRegExp = RegExp(r'https?://\S+\b');
|
||||
final Iterable<Match> matches =
|
||||
urlRegExp.allMatches(currentDesc.rawText);
|
||||
|
||||
int previousEndIndex = 0;
|
||||
for (Match match in matches) {
|
||||
for (final Match match in matches) {
|
||||
if (match.start > previousEndIndex) {
|
||||
spanChildren.add(TextSpan(
|
||||
text: currentDesc.rawText
|
||||
@ -172,11 +170,12 @@ class IntroDetail extends StatelessWidget {
|
||||
text: currentDesc.rawText.substring(previousEndIndex)));
|
||||
}
|
||||
|
||||
TextSpan result = TextSpan(children: spanChildren);
|
||||
final TextSpan result = TextSpan(children: spanChildren);
|
||||
return result;
|
||||
case 2:
|
||||
final colorSchemePrimary = Theme.of(context).colorScheme.primary;
|
||||
final heroTag = Utils.makeHeroTag(currentDesc.bizId);
|
||||
final Color colorSchemePrimary =
|
||||
Theme.of(context).colorScheme.primary;
|
||||
final String heroTag = Utils.makeHeroTag(currentDesc.bizId);
|
||||
return TextSpan(
|
||||
text: '@${currentDesc.rawText}',
|
||||
style: TextStyle(color: colorSchemePrimary),
|
||||
|
||||
@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
|
||||
class MenuRow extends StatelessWidget {
|
||||
final bool? loadingStatus;
|
||||
const MenuRow({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.loadingStatus,
|
||||
}) : super(key: key);
|
||||
});
|
||||
final bool? loadingStatus;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -50,7 +50,7 @@ class MenuRow extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget actionRowLineItem(
|
||||
context, Function? onTap, bool? loadingStatus, String? text,
|
||||
BuildContext context, Function? onTap, bool? loadingStatus, String? text,
|
||||
{bool selectStatus = false}) {
|
||||
return Material(
|
||||
color: selectStatus
|
||||
@ -97,18 +97,18 @@ class MenuRow extends StatelessWidget {
|
||||
}
|
||||
|
||||
class ActionRowLineItem extends StatelessWidget {
|
||||
const ActionRowLineItem({
|
||||
super.key,
|
||||
this.selectStatus,
|
||||
this.onTap,
|
||||
this.text,
|
||||
this.loadingStatus = false,
|
||||
});
|
||||
final bool? selectStatus;
|
||||
final Function? onTap;
|
||||
final bool? loadingStatus;
|
||||
final String? text;
|
||||
|
||||
const ActionRowLineItem(
|
||||
{super.key,
|
||||
this.selectStatus,
|
||||
this.onTap,
|
||||
this.text,
|
||||
this.loadingStatus = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
|
||||
@ -4,11 +4,6 @@ import 'package:pilipala/models/video_detail_res.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
|
||||
class PagesPanel extends StatefulWidget {
|
||||
final List<Part> pages;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
const PagesPanel({
|
||||
super.key,
|
||||
required this.pages,
|
||||
@ -16,6 +11,10 @@ class PagesPanel extends StatefulWidget {
|
||||
this.sheetHeight,
|
||||
this.changeFuc,
|
||||
});
|
||||
final List<Part> pages;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
@override
|
||||
State<PagesPanel> createState() => _PagesPanelState();
|
||||
@ -25,7 +24,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
late List<Part> episodes;
|
||||
late int cid;
|
||||
late int currentIndex;
|
||||
String heroTag = Get.arguments['heroTag'];
|
||||
final String heroTag = Get.arguments['heroTag'];
|
||||
late VideoDetailController _videoDetailController;
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@ -35,11 +34,11 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
cid = widget.cid!;
|
||||
episodes = widget.pages;
|
||||
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
|
||||
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||
_videoDetailController.cid.listen((p0) {
|
||||
currentIndex = episodes.indexWhere((Part e) => e.cid == cid);
|
||||
_videoDetailController.cid.listen((int p0) {
|
||||
cid = p0;
|
||||
setState(() {});
|
||||
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||
currentIndex = episodes.indexWhere((Part e) => e.cid == cid);
|
||||
});
|
||||
}
|
||||
|
||||
@ -60,7 +59,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 2),
|
||||
child: Row(
|
||||
@ -133,7 +132,8 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
itemCount: episodes.length,
|
||||
itemBuilder: (context, index) {
|
||||
itemBuilder:
|
||||
(BuildContext context, int index) {
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
changeFucCall(
|
||||
@ -191,7 +191,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: widget.pages.length,
|
||||
itemExtent: 150,
|
||||
itemBuilder: ((context, i) {
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
return Container(
|
||||
width: 150,
|
||||
margin: const EdgeInsets.only(right: 10),
|
||||
@ -205,8 +205,8 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8, horizontal: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
if (i == currentIndex) ...[
|
||||
children: <Widget>[
|
||||
if (i == currentIndex) ...<Widget>[
|
||||
Image.asset(
|
||||
'assets/images/live.gif',
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
@ -231,7 +231,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
|
||||
@ -6,11 +6,6 @@ import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
class SeasonPanel extends StatefulWidget {
|
||||
final UgcSeason ugcSeason;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
const SeasonPanel({
|
||||
super.key,
|
||||
required this.ugcSeason,
|
||||
@ -18,6 +13,10 @@ class SeasonPanel extends StatefulWidget {
|
||||
this.sheetHeight,
|
||||
this.changeFuc,
|
||||
});
|
||||
final UgcSeason ugcSeason;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
@override
|
||||
State<SeasonPanel> createState() => _SeasonPanelState();
|
||||
@ -27,7 +26,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
late List<EpisodeItem> episodes;
|
||||
late int cid;
|
||||
late int currentIndex;
|
||||
String heroTag = Get.arguments['heroTag'];
|
||||
final String heroTag = Get.arguments['heroTag'];
|
||||
late VideoDetailController _videoDetailController;
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
final ItemScrollController itemScrollController = ItemScrollController();
|
||||
@ -41,9 +40,9 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
/// 根据 cid 找到对应集,找到对应 episodes
|
||||
/// 有多个episodes时,只显示其中一个
|
||||
/// TODO 同时显示多个合集
|
||||
List<SectionItem> sections = widget.ugcSeason.sections!;
|
||||
final List<SectionItem> sections = widget.ugcSeason.sections!;
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||
for (int j = 0; j < episodesList.length; j++) {
|
||||
if (episodesList[j].cid == cid) {
|
||||
episodes = episodesList;
|
||||
@ -56,22 +55,21 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
// episodes = widget.ugcSeason.sections!
|
||||
// .firstWhere((e) => e.seasonId == widget.ugcSeason.id)
|
||||
// .episodes!;
|
||||
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||
_videoDetailController.cid.listen((p0) {
|
||||
currentIndex = episodes.indexWhere((EpisodeItem e) => e.cid == cid);
|
||||
_videoDetailController.cid.listen((int p0) {
|
||||
cid = p0;
|
||||
setState(() {});
|
||||
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||
currentIndex = episodes.indexWhere((EpisodeItem e) => e.cid == cid);
|
||||
});
|
||||
}
|
||||
|
||||
void changeFucCall(item, i) async {
|
||||
void changeFucCall(item, int i) async {
|
||||
await widget.changeFuc!(
|
||||
IdUtils.av2bv(item.aid),
|
||||
item.cid,
|
||||
item.aid,
|
||||
);
|
||||
currentIndex = i;
|
||||
setState(() {});
|
||||
Get.back();
|
||||
setState(() {});
|
||||
}
|
||||
@ -84,7 +82,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(builder: (context) {
|
||||
return Builder(builder: (BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(
|
||||
top: 8,
|
||||
@ -136,7 +134,8 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
child: Material(
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: episodes.length,
|
||||
itemBuilder: (context, index) => ListTile(
|
||||
itemBuilder: (BuildContext context, int index) =>
|
||||
ListTile(
|
||||
onTap: () =>
|
||||
changeFucCall(episodes[index], index),
|
||||
dense: false,
|
||||
@ -174,7 +173,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||
child: Row(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
'合集:${widget.ugcSeason.title!}',
|
||||
|
||||
@ -7,13 +7,7 @@ import 'package:pilipala/common/widgets/overlay_pop.dart';
|
||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||
import './controller.dart';
|
||||
|
||||
class RelatedVideoPanel extends StatefulWidget {
|
||||
const RelatedVideoPanel({super.key});
|
||||
@override
|
||||
State<RelatedVideoPanel> createState() => _RelatedVideoPanelState();
|
||||
}
|
||||
|
||||
class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
|
||||
class RelatedVideoPanel extends StatelessWidget {
|
||||
final ReleatedController _releatedController =
|
||||
Get.put(ReleatedController(), tag: Get.arguments['heroTag']);
|
||||
|
||||
@ -21,7 +15,7 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: _releatedController.queryRelatedVideo(),
|
||||
builder: (context, snapshot) {
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SliverToBoxAdapter(child: SizedBox());
|
||||
@ -72,7 +66,7 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
|
||||
|
||||
OverlayEntry _createPopupDialog(videoItem) {
|
||||
return OverlayEntry(
|
||||
builder: (context) => AnimatedDialog(
|
||||
builder: (BuildContext context) => AnimatedDialog(
|
||||
closeFn: _releatedController.popupDialog?.remove,
|
||||
child: OverlayPop(
|
||||
videoItem: videoItem,
|
||||
|
||||
@ -41,8 +41,8 @@ class VideoReplyController extends GetxController {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
int deaultReplySortIndex =
|
||||
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
|
||||
final int deaultReplySortIndex =
|
||||
setting.get(SettingBoxKey.replySortType, defaultValue: 0) as int;
|
||||
_sortType = ReplySortType.values[deaultReplySortIndex];
|
||||
sortTypeTitle.value = _sortType.titles;
|
||||
sortTypeLabel.value = _sortType.labels;
|
||||
@ -56,7 +56,7 @@ class VideoReplyController extends GetxController {
|
||||
if (noMore.value == '没有更多了') {
|
||||
return;
|
||||
}
|
||||
var res = await ReplyHttp.replyList(
|
||||
final res = await ReplyHttp.replyList(
|
||||
oid: aid!,
|
||||
pageNum: currentPage + 1,
|
||||
ps: ps,
|
||||
@ -64,7 +64,7 @@ class VideoReplyController extends GetxController {
|
||||
sort: _sortType.index,
|
||||
);
|
||||
if (res['status']) {
|
||||
List<ReplyItemModel> replies = res['data'].replies;
|
||||
final List<ReplyItemModel> replies = res['data'].replies;
|
||||
if (replies.isNotEmpty) {
|
||||
noMore.value = '加载中...';
|
||||
|
||||
@ -84,9 +84,8 @@ class VideoReplyController extends GetxController {
|
||||
if (type == 'init') {
|
||||
// 添加置顶回复
|
||||
if (res['data'].upper.top != null) {
|
||||
bool flag = res['data']
|
||||
.topReplies
|
||||
.any((reply) => reply.rpid == res['data'].upper.top.rpid);
|
||||
final bool flag = res['data'].topReplies.any((ReplyItemModel reply) =>
|
||||
reply.rpid == res['data'].upper.top.rpid) as bool;
|
||||
if (!flag) {
|
||||
replies.insert(0, res['data'].upper.top);
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import 'package:pilipala/common/skeleton/video_reply.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'controller.dart';
|
||||
@ -107,7 +107,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
|
||||
// 展示二级回复
|
||||
void replyReply(replyItem) {
|
||||
VideoDetailController videoDetailCtr =
|
||||
final VideoDetailController videoDetailCtr =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
if (replyItem != null) {
|
||||
videoDetailCtr.oid = replyItem.oid;
|
||||
@ -193,7 +193,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
builder: (BuildContext context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
var data = snapshot.data;
|
||||
if (data['status']) {
|
||||
@ -203,13 +203,13 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
_videoReplyController.replyList.isEmpty
|
||||
? SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
(BuildContext context, index) {
|
||||
return const VideoReplySkeleton();
|
||||
}, childCount: 5),
|
||||
)
|
||||
: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
(BuildContext context, index) {
|
||||
double bottom =
|
||||
MediaQuery.of(context).padding.bottom;
|
||||
if (index ==
|
||||
@ -262,7 +262,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
} else {
|
||||
// 骨架屏
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, index) {
|
||||
return const VideoReplySkeleton();
|
||||
}, childCount: 5),
|
||||
);
|
||||
@ -318,12 +319,11 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
}
|
||||
|
||||
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
_MySliverPersistentHeaderDelegate({required this.child});
|
||||
final double _minExtent = 45;
|
||||
final double _maxExtent = 45;
|
||||
final Widget child;
|
||||
|
||||
_MySliverPersistentHeaderDelegate({required this.child});
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
|
||||
@ -9,7 +9,7 @@ import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/pages/preview/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
@ -27,8 +27,8 @@ class ReplyItem extends StatelessWidget {
|
||||
this.showReplyRow = true,
|
||||
this.replyReply,
|
||||
this.replyType,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
final ReplyItemModel? replyItem;
|
||||
final Function? addReply;
|
||||
final String? replyLevel;
|
||||
@ -68,7 +68,7 @@ class ReplyItem extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget lfAvtar(context, heroTag) {
|
||||
Widget lfAvtar(BuildContext context, String heroTag) {
|
||||
return Stack(
|
||||
children: [
|
||||
Hero(
|
||||
@ -117,8 +117,8 @@ class ReplyItem extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget content(context) {
|
||||
String heroTag = Utils.makeHeroTag(replyItem!.mid);
|
||||
Widget content(BuildContext context) {
|
||||
final String heroTag = Utils.makeHeroTag(replyItem!.mid);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
@ -260,7 +260,7 @@ class ReplyItem extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
Text(
|
||||
Utils.dateFormat(replyItem!.ctime),
|
||||
style: TextStyle(
|
||||
@ -291,7 +291,6 @@ class ReplyItem extends StatelessWidget {
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
||||
child: SelectableRegion(
|
||||
magnifierConfiguration: const TextMagnifierConfiguration(),
|
||||
focusNode: FocusNode(),
|
||||
selectionControls: MaterialTextSelectionControls(),
|
||||
child: Text.rich(
|
||||
@ -340,9 +339,9 @@ class ReplyItem extends StatelessWidget {
|
||||
}
|
||||
|
||||
// 感谢、回复、复制
|
||||
Widget bottonAction(context, replyControl) {
|
||||
Widget bottonAction(BuildContext context, replyControl) {
|
||||
return Row(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
const SizedBox(width: 32),
|
||||
SizedBox(
|
||||
height: 32,
|
||||
@ -422,7 +421,7 @@ class ReplyItemRow extends StatelessWidget {
|
||||
this.replyItem,
|
||||
this.replyReply,
|
||||
});
|
||||
List? replies;
|
||||
final List? replies;
|
||||
ReplyControl? replyControl;
|
||||
// int? f_rpid;
|
||||
ReplyItemModel? replyItem;
|
||||
@ -430,8 +429,8 @@ class ReplyItemRow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isShow = replyControl!.isShow!;
|
||||
int extraRow = replyControl != null && isShow ? 1 : 0;
|
||||
final bool isShow = replyControl!.isShow!;
|
||||
final int extraRow = replyControl != null && isShow ? 1 : 0;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
|
||||
child: Material(
|
||||
@ -443,7 +442,7 @@ class ReplyItemRow extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (replies!.isNotEmpty)
|
||||
for (var i = 0; i < replies!.length; i++) ...[
|
||||
for (int i = 0; i < replies!.length; i++) ...[
|
||||
InkWell(
|
||||
// 一楼点击评论展开评论详情
|
||||
onTap: () => replyReply!(replyItem),
|
||||
@ -472,7 +471,7 @@ class ReplyItemRow extends StatelessWidget {
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
feedBack();
|
||||
String heroTag =
|
||||
final String heroTag =
|
||||
Utils.makeHeroTag(replies![i].member.mid);
|
||||
Get.toNamed(
|
||||
'/member?mid=${replies![i].member.mid}',
|
||||
@ -539,7 +538,7 @@ InlineSpan buildContent(
|
||||
// replyItem 当前回复内容
|
||||
// replyReply 查看二楼回复(回复详情)回调
|
||||
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
||||
var content = replyItem.content;
|
||||
final content = replyItem.content;
|
||||
if (content.emote.isEmpty &&
|
||||
content.atNameToMid.isEmpty &&
|
||||
content.jumpUrl.isEmpty &&
|
||||
@ -552,7 +551,7 @@ InlineSpan buildContent(
|
||||
() => replyReply(replyItem.root == 0 ? replyItem : fReplyItem),
|
||||
);
|
||||
}
|
||||
List<InlineSpan> spanChilds = [];
|
||||
final List<InlineSpan> spanChilds = <InlineSpan>[];
|
||||
bool hasMatchMember = true;
|
||||
|
||||
// 投票
|
||||
@ -590,11 +589,11 @@ InlineSpan buildContent(
|
||||
content.message.splitMapJoin(
|
||||
RegExp(r"\[.*?\]"),
|
||||
onMatch: (Match match) {
|
||||
String matchStr = match[0]!;
|
||||
final String matchStr = match[0]!;
|
||||
if (content.emote.isNotEmpty &&
|
||||
matchStr.indexOf('[') == matchStr.lastIndexOf('[') &&
|
||||
matchStr.indexOf(']') == matchStr.lastIndexOf(']')) {
|
||||
int size = content.emote[matchStr]['meta']['size'];
|
||||
final int size = content.emote[matchStr]['meta']['size'];
|
||||
if (content.emote.keys.contains(matchStr)) {
|
||||
spanChilds.add(
|
||||
WidgetSpan(
|
||||
@ -628,7 +627,7 @@ InlineSpan buildContent(
|
||||
// 匹配@用户
|
||||
String matchMember = str;
|
||||
if (content.atNameToMid.isNotEmpty) {
|
||||
List atNameToMidKeys = content.atNameToMid.keys.toList();
|
||||
final List atNameToMidKeys = content.atNameToMid.keys.toList();
|
||||
RegExp reg = RegExp(atNameToMidKeys.map((key) => key).join('|'));
|
||||
// if (!content.message.contains(':')) {
|
||||
// reg = RegExp(r"@.*( |:)");
|
||||
@ -667,7 +666,7 @@ InlineSpan buildContent(
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
String heroTag = Utils.makeHeroTag(value);
|
||||
final String heroTag = Utils.makeHeroTag(value);
|
||||
Get.toNamed(
|
||||
'/member?mid=$value',
|
||||
arguments: {'face': '', 'heroTag': heroTag},
|
||||
@ -693,8 +692,8 @@ InlineSpan buildContent(
|
||||
// 匹配 jumpUrl
|
||||
String matchUrl = matchMember;
|
||||
if (content.jumpUrl.isNotEmpty && hasMatchMember) {
|
||||
List urlKeys = content.jumpUrl.keys.toList().reversed.toList();
|
||||
for (var index = 0; index < urlKeys.length; index++) {
|
||||
final List urlKeys = content.jumpUrl.keys.toList().reversed.toList();
|
||||
for (int index = 0; index < urlKeys.length; index++) {
|
||||
var i = urlKeys[index];
|
||||
if (i.contains('?')) {
|
||||
urlKeys[index] = i.replaceAll('?', '\\?');
|
||||
@ -711,14 +710,14 @@ InlineSpan buildContent(
|
||||
RegExp(urlKeys.map((key) => key).join("|")),
|
||||
// RegExp('What does the fox say\\?'),
|
||||
onMatch: (Match match) {
|
||||
String matchStr = match[0]!;
|
||||
final String matchStr = match[0]!;
|
||||
String appUrlSchema = '';
|
||||
if (content.jumpUrl[matchStr] != null) {
|
||||
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
|
||||
}
|
||||
// 默认不显示关键词
|
||||
bool enableWordRe =
|
||||
setting.get(SettingBoxKey.enableWordRe, defaultValue: false);
|
||||
final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
|
||||
defaultValue: false) as bool;
|
||||
if (content.jumpUrl[matchStr] != null) {
|
||||
spanChilds.add(
|
||||
TextSpan(
|
||||
@ -731,9 +730,9 @@ InlineSpan buildContent(
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
if (appUrlSchema == '') {
|
||||
String str = Uri.parse(matchStr).pathSegments[0];
|
||||
Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||
List matchKeys = matchRes.keys.toList();
|
||||
final String str = Uri.parse(matchStr).pathSegments[0];
|
||||
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||
final List matchKeys = matchRes.keys.toList();
|
||||
if (matchKeys.isNotEmpty) {
|
||||
if (matchKeys.first == 'BV') {
|
||||
Get.toNamed(
|
||||
@ -834,8 +833,8 @@ InlineSpan buildContent(
|
||||
|
||||
// 图片渲染
|
||||
if (content.pictures.isNotEmpty) {
|
||||
List<String> picList = [];
|
||||
int len = content.pictures.length;
|
||||
final List<String> picList = <String>[];
|
||||
final int len = content.pictures.length;
|
||||
if (len == 1) {
|
||||
Map pictureItem = content.pictures.first;
|
||||
picList.add(pictureItem['img_src']);
|
||||
@ -843,13 +842,13 @@ InlineSpan buildContent(
|
||||
spanChilds.add(
|
||||
WidgetSpan(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, BoxConstraints box) {
|
||||
builder: (BuildContext context, BoxConstraints box) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
useSafeArea: false,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
return ImagePreview(initialPage: 0, imgList: picList);
|
||||
},
|
||||
);
|
||||
|
||||
@ -25,12 +25,12 @@ class _ZanButtonState extends State<ZanButton> {
|
||||
Future onLikeReply() async {
|
||||
feedBack();
|
||||
// SmartDialog.showLoading(msg: 'pilipala ...');
|
||||
ReplyItemModel replyItem = widget.replyItem!;
|
||||
int oid = replyItem.oid!;
|
||||
int rpid = replyItem.rpid!;
|
||||
final ReplyItemModel replyItem = widget.replyItem!;
|
||||
final int oid = replyItem.oid!;
|
||||
final int rpid = replyItem.rpid!;
|
||||
// 1 已点赞 2 不喜欢 0 未操作
|
||||
int action = replyItem.action == 0 ? 1 : 0;
|
||||
var res = await ReplyHttp.likeReply(
|
||||
final int action = replyItem.action == 0 ? 1 : 0;
|
||||
final res = await ReplyHttp.likeReply(
|
||||
type: widget.replyType!.index, oid: oid, rpid: rpid, action: action);
|
||||
// SmartDialog.dismiss();
|
||||
if (res['status']) {
|
||||
@ -47,19 +47,23 @@ class _ZanButtonState extends State<ZanButton> {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
bool isProcessing = false;
|
||||
void Function()? handleState(Future Function() action) {
|
||||
return isProcessing ? null : () async {
|
||||
setState(() => isProcessing = true);
|
||||
await action();
|
||||
setState(() => isProcessing = false);
|
||||
};
|
||||
return isProcessing
|
||||
? null
|
||||
: () async {
|
||||
setState(() => isProcessing = true);
|
||||
await action();
|
||||
setState(() => isProcessing = false);
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var color = Theme.of(context).colorScheme.outline;
|
||||
var primary = Theme.of(context).colorScheme.primary;
|
||||
final ThemeData t = Theme.of(context);
|
||||
final Color color = t.colorScheme.outline;
|
||||
final Color primary = t.colorScheme.primary;
|
||||
return SizedBox(
|
||||
height: 32,
|
||||
child: TextButton(
|
||||
@ -79,12 +83,14 @@ class _ZanButtonState extends State<ZanButton> {
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return ScaleTransition(scale: animation, child: child);
|
||||
},
|
||||
child: Text(widget.replyItem!.like.toString(),
|
||||
key: ValueKey<int>(widget.replyItem!.like!),
|
||||
style: TextStyle(
|
||||
color: widget.replyItem!.action == 1 ? primary : color,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize)),
|
||||
child: Text(
|
||||
widget.replyItem!.like.toString(),
|
||||
key: ValueKey<int>(widget.replyItem!.like!),
|
||||
style: TextStyle(
|
||||
color: widget.replyItem!.action == 1 ? primary : color,
|
||||
fontSize: t.textTheme.labelSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -31,14 +31,14 @@ class VideoReplyReplyController extends GetxController {
|
||||
currentPage = 0;
|
||||
}
|
||||
isLoadingMore = true;
|
||||
var res = await ReplyHttp.replyReplyList(
|
||||
final res = await ReplyHttp.replyReplyList(
|
||||
oid: aid!,
|
||||
root: rpid!,
|
||||
pageNum: currentPage + 1,
|
||||
type: replyType.index,
|
||||
);
|
||||
if (res['status']) {
|
||||
List<ReplyItemModel> replies = res['data'].replies;
|
||||
final List<ReplyItemModel> replies = res['data'].replies;
|
||||
if (replies.isNotEmpty) {
|
||||
noMore.value = '加载中...';
|
||||
if (replyList.length == res['data'].page.count) {
|
||||
@ -12,13 +12,6 @@ import 'package:pilipala/utils/storage.dart';
|
||||
import 'controller.dart';
|
||||
|
||||
class VideoReplyReplyPanel extends StatefulWidget {
|
||||
final int? oid;
|
||||
final int? rpid;
|
||||
final Function? closePanel;
|
||||
final ReplyItemModel? firstFloor;
|
||||
final String? source;
|
||||
final ReplyType? replyType;
|
||||
|
||||
const VideoReplyReplyPanel({
|
||||
this.oid,
|
||||
this.rpid,
|
||||
@ -28,6 +21,12 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
||||
this.replyType,
|
||||
super.key,
|
||||
});
|
||||
final int? oid;
|
||||
final int? rpid;
|
||||
final Function? closePanel;
|
||||
final ReplyItemModel? firstFloor;
|
||||
final String? source;
|
||||
final ReplyType? replyType;
|
||||
|
||||
@override
|
||||
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
|
||||
@ -36,7 +35,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
||||
class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
late VideoReplyReplyController _videoReplyReplyController;
|
||||
late AnimationController replyAnimationCtl;
|
||||
Box localCache = GStrorage.localCache;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
Future? _futureBuilderFuture;
|
||||
late ScrollController scrollController;
|
||||
@ -87,7 +86,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
padding: const EdgeInsets.only(left: 12, right: 2),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
children: <Widget>[
|
||||
const Text('评论详情'),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close, size: 20),
|
||||
@ -138,15 +137,15 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
],
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
builder: (BuildContext context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map data = snapshot.data as Map;
|
||||
final Map data = snapshot.data as Map;
|
||||
if (data['status']) {
|
||||
// 请求成功
|
||||
return Obx(
|
||||
() => SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
(BuildContext context, int index) {
|
||||
if (index ==
|
||||
_videoReplyReplyController
|
||||
.replyList.length) {
|
||||
@ -204,8 +203,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
} else {
|
||||
// 骨架屏
|
||||
return SliverList(
|
||||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return const VideoReplySkeleton();
|
||||
}, childCount: 8),
|
||||
);
|
||||
@ -8,6 +8,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:nil/nil.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/http/user.dart';
|
||||
import 'package:pilipala/models/common/search_type.dart';
|
||||
@ -47,16 +48,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
PlayerStatus playerStatus = PlayerStatus.playing;
|
||||
double doubleOffset = 0;
|
||||
|
||||
Box localCache = GStrorage.localCache;
|
||||
Box setting = GStrorage.setting;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
final Box<dynamic> setting = GStrorage.setting;
|
||||
late double statusBarHeight;
|
||||
final videoHeight = Get.size.width * 9 / 16;
|
||||
final double videoHeight = Get.size.width * 9 / 16;
|
||||
late Future _futureBuilderFuture;
|
||||
// 自动退出全屏
|
||||
late bool autoExitFullcreen;
|
||||
late bool autoPlayEnable;
|
||||
late bool autoPiP;
|
||||
final floating = Floating();
|
||||
final Floating floating = Floating();
|
||||
// 生命周期监听
|
||||
late final AppLifecycleListener _lifecycleListener;
|
||||
|
||||
@ -105,7 +106,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
appbarStream = StreamController<double>();
|
||||
_extendNestCtr.addListener(
|
||||
() {
|
||||
double offset = _extendNestCtr.position.pixels;
|
||||
final double offset = _extendNestCtr.position.pixels;
|
||||
appbarStream.add(offset);
|
||||
},
|
||||
);
|
||||
@ -202,7 +203,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
// 离开当前页面时
|
||||
void didPushNext() async {
|
||||
/// 开启
|
||||
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)) {
|
||||
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)
|
||||
as bool) {
|
||||
videoDetailController.brightness = plPlayerController!.brightness.value;
|
||||
}
|
||||
if (plPlayerController != null) {
|
||||
@ -218,7 +220,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
// 返回当前页面时
|
||||
void didPopNext() async {
|
||||
videoDetailController.isFirstTime = false;
|
||||
bool autoplay = autoPlayEnable;
|
||||
final bool autoplay = autoPlayEnable;
|
||||
videoDetailController.playerInit(autoplay: autoplay);
|
||||
|
||||
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
||||
@ -238,7 +240,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
VideoDetailPage.routeObserver
|
||||
.subscribe(this, ModalRoute.of(context) as PageRoute);
|
||||
.subscribe(this, ModalRoute.of(context)! as PageRoute);
|
||||
}
|
||||
|
||||
void _handleTransition(String name) {
|
||||
@ -253,8 +255,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
}
|
||||
|
||||
void autoEnterPip() {
|
||||
var routePath = Get.currentRoute;
|
||||
bool isPortrait =
|
||||
final String routePath = Get.currentRoute;
|
||||
final bool isPortrait =
|
||||
MediaQuery.of(context).orientation == Orientation.portrait;
|
||||
|
||||
/// TODO 横屏全屏状态下误触pip
|
||||
@ -269,7 +271,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final videoHeight = MediaQuery.of(context).size.width * 9 / 16;
|
||||
final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
|
||||
final double pinnedHeaderHeight =
|
||||
statusBarHeight + kToolbarHeight + videoHeight;
|
||||
if (MediaQuery.of(context).orientation == Orientation.landscape ||
|
||||
@ -303,19 +305,20 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
headerSliverBuilder:
|
||||
(BuildContext _context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
Obx(() => SliverAppBar(
|
||||
Obx(
|
||||
() => SliverAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
// 假装使用一个非空变量,避免Obx检测不到而罢工
|
||||
pinned: videoDetailController
|
||||
.autoPlay.value ^ false ^ videoDetailController
|
||||
.autoPlay.value,
|
||||
pinned: videoDetailController.autoPlay.value ^
|
||||
false ^
|
||||
videoDetailController.autoPlay.value,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
expandedHeight: MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape ||
|
||||
plPlayerController?.isFullScreen.value == true
|
||||
? MediaQuery.of(context).size.height -
|
||||
? MediaQuery.sizeOf(context).height -
|
||||
(MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape
|
||||
? 0
|
||||
@ -338,14 +341,17 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
}
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (context, boxConstraints) {
|
||||
double maxWidth = boxConstraints.maxWidth;
|
||||
double maxHeight = boxConstraints.maxHeight;
|
||||
builder: (BuildContext context,
|
||||
BoxConstraints boxConstraints) {
|
||||
final double maxWidth = boxConstraints.maxWidth;
|
||||
final double maxHeight =
|
||||
boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: ((context, snapshot) {
|
||||
builder: (BuildContext context,
|
||||
AsyncSnapshot snapshot) {
|
||||
if (snapshot.hasData &&
|
||||
snapshot.data['status']) {
|
||||
return Obx(
|
||||
@ -378,7 +384,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
}),
|
||||
},
|
||||
),
|
||||
|
||||
Obx(
|
||||
@ -418,6 +424,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
child: AppBar(
|
||||
primary: false,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
actions: [
|
||||
@ -468,7 +476,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
);
|
||||
},
|
||||
)),
|
||||
))),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
// pinnedHeaderSliverHeightBuilder: () {
|
||||
@ -479,11 +489,11 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
/// 不收回
|
||||
pinnedHeaderSliverHeightBuilder: () {
|
||||
return plPlayerController?.isFullScreen.value == true
|
||||
? MediaQuery.of(context).size.height
|
||||
? MediaQuery.sizeOf(context).height
|
||||
: pinnedHeaderHeight;
|
||||
},
|
||||
onlyOneScrollInBody: true,
|
||||
body: Container(
|
||||
body: ColoredBox(
|
||||
key: Key(heroTag),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
@ -509,9 +519,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: videoDetailController.tabCtr,
|
||||
children: [
|
||||
children: <Widget>[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
return CustomScrollView(
|
||||
key: const PageStorageKey<String>('简介'),
|
||||
slivers: <Widget>[
|
||||
@ -544,7 +554,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
.withOpacity(0.06),
|
||||
),
|
||||
),
|
||||
const RelatedVideoPanel(),
|
||||
RelatedVideoPanel(),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -581,7 +591,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
Widget childWhenEnabled = FutureBuilder(
|
||||
key: Key(heroTag),
|
||||
future: _futureBuilderFuture,
|
||||
builder: ((context, snapshot) {
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.hasData && snapshot.data['status']) {
|
||||
return Obx(
|
||||
() => !videoDetailController.autoPlay.value
|
||||
@ -603,9 +613,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
return nil;
|
||||
}
|
||||
}),
|
||||
},
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
return PiPSwitcher(
|
||||
|
||||
@ -16,7 +16,7 @@ class ScrollAppBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
||||
final videoHeight = MediaQuery.of(context).size.width * 9 / 16;
|
||||
final videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
|
||||
return Positioned(
|
||||
top: -videoHeight + scrollVal + kToolbarHeight + 0.5,
|
||||
left: 0,
|
||||
|
||||
@ -19,15 +19,15 @@ import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/http/danmaku.dart';
|
||||
|
||||
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||
final PlPlayerController? controller;
|
||||
final VideoDetailController? videoDetailCtr;
|
||||
final Floating? floating;
|
||||
const HeaderControl({
|
||||
this.controller,
|
||||
this.videoDetailCtr,
|
||||
this.floating,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
final PlPlayerController? controller;
|
||||
final VideoDetailController? videoDetailCtr;
|
||||
final Floating? floating;
|
||||
|
||||
@override
|
||||
State<HeaderControl> createState() => _HeaderControlState();
|
||||
@ -42,9 +42,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
TextStyle subTitleStyle = const TextStyle(fontSize: 12);
|
||||
TextStyle titleStyle = const TextStyle(fontSize: 14);
|
||||
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
||||
Box localCache = GStrorage.localCache;
|
||||
Box videoStorage = GStrorage.video;
|
||||
late List speedsList;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
final Box<dynamic> videoStorage = GStrorage.video;
|
||||
late List<double> speedsList;
|
||||
double buttonSpace = 8;
|
||||
|
||||
@override
|
||||
@ -71,7 +71,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: Center(
|
||||
@ -118,7 +118,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
// ),
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
var res = await UserHttp.toViewLater(
|
||||
final res = await UserHttp.toViewLater(
|
||||
bvid: widget.videoDetailCtr!.bvid);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
Get.back();
|
||||
@ -187,11 +187,12 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
bool isSending = false; // 追踪是否正在发送
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
// TODO: 支持更多类型和颜色的弹幕
|
||||
return AlertDialog(
|
||||
title: const Text('发送弹幕(测试)'),
|
||||
content: StatefulBuilder(builder: (context, StateSetter setState) {
|
||||
content: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return TextField(
|
||||
controller: textController,
|
||||
);
|
||||
@ -204,12 +205,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
StatefulBuilder(builder: (context, StateSetter setState) {
|
||||
StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return TextButton(
|
||||
onPressed: isSending
|
||||
? null
|
||||
: () async {
|
||||
String msg = textController.text;
|
||||
final String msg = textController.text;
|
||||
if (msg.isEmpty) {
|
||||
SmartDialog.showToast('弹幕内容不能为空');
|
||||
return;
|
||||
@ -222,10 +224,10 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
});
|
||||
//修改按钮文字
|
||||
// SmartDialog.showToast('弹幕发送中,\n$msg');
|
||||
dynamic res = await DanmakaHttp.shootDanmaku(
|
||||
oid: widget.videoDetailCtr!.cid!.value,
|
||||
final dynamic res = await DanmakaHttp.shootDanmaku(
|
||||
oid: widget.videoDetailCtr!.cid.value,
|
||||
msg: textController.text,
|
||||
bvid: widget.videoDetailCtr!.bvid!,
|
||||
bvid: widget.videoDetailCtr!.bvid,
|
||||
progress:
|
||||
widget.controller!.position.value.inMilliseconds,
|
||||
type: 1,
|
||||
@ -262,20 +264,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
|
||||
/// 选择倍速
|
||||
void showSetSpeedSheet() {
|
||||
double currentSpeed = widget.controller!.playbackSpeed;
|
||||
final double currentSpeed = widget.controller!.playbackSpeed;
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('播放速度'),
|
||||
content: StatefulBuilder(builder: (context, StateSetter setState) {
|
||||
content: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return Wrap(
|
||||
alignment: WrapAlignment.start,
|
||||
spacing: 8,
|
||||
runSpacing: 2,
|
||||
children: [
|
||||
for (var i in speedsList) ...[
|
||||
if (i == currentSpeed) ...[
|
||||
for (final double i in speedsList) ...<Widget>[
|
||||
if (i == currentSpeed) ...<Widget>[
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
// setState(() => currentSpeed = i),
|
||||
@ -298,7 +300,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
],
|
||||
);
|
||||
}),
|
||||
actions: [
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => SmartDialog.dismiss(),
|
||||
child: Text(
|
||||
@ -321,18 +323,18 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
|
||||
/// 选择画质
|
||||
void showSetVideoQa() {
|
||||
List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
final VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
||||
|
||||
/// 总质量分类
|
||||
int totalQaSam = videoFormat.length;
|
||||
final int totalQaSam = videoFormat.length;
|
||||
|
||||
/// 可用的质量分类
|
||||
int userfulQaSam = 0;
|
||||
List<VideoItem> video = videoInfo.dash!.video!;
|
||||
Set<int> idSet = {};
|
||||
for (var item in video) {
|
||||
int id = item.id!;
|
||||
final List<VideoItem> video = videoInfo.dash!.video!;
|
||||
final Set<int> idSet = {};
|
||||
for (final VideoItem item in video) {
|
||||
final int id = item.id!;
|
||||
if (!idSet.contains(id)) {
|
||||
idSet.add(id);
|
||||
userfulQaSam++;
|
||||
@ -354,7 +356,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 45,
|
||||
child: GestureDetector(
|
||||
@ -380,7 +382,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
child: Scrollbar(
|
||||
child: ListView(
|
||||
children: [
|
||||
for (var i = 0; i < totalQaSam; i++) ...[
|
||||
for (int i = 0; i < totalQaSam; i++) ...[
|
||||
ListTile(
|
||||
onTap: () {
|
||||
if (currentVideoQa.code ==
|
||||
@ -427,9 +429,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
|
||||
/// 选择音质
|
||||
void showSetAudioQa() {
|
||||
AudioQuality currentAudioQa = widget.videoDetailCtr!.currentAudioQa!;
|
||||
|
||||
List<AudioItem> audio = videoInfo.dash!.audio!;
|
||||
final AudioQuality currentAudioQa = widget.videoDetailCtr!.currentAudioQa!;
|
||||
final List<AudioItem> audio = videoInfo.dash!.audio!;
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
elevation: 0,
|
||||
@ -445,18 +446,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 45,
|
||||
child: Center(child: Text('选择音质', style: titleStyle))),
|
||||
Expanded(
|
||||
child: Material(
|
||||
child: ListView(
|
||||
children: [
|
||||
for (var i in audio) ...[
|
||||
children: <Widget>[
|
||||
for (final AudioItem i in audio) ...<Widget>[
|
||||
ListTile(
|
||||
onTap: () {
|
||||
if (currentAudioQa.code == i.id) return;
|
||||
if (currentAudioQa.code == i.id) {
|
||||
return;
|
||||
}
|
||||
final int quality = i.id!;
|
||||
widget.videoDetailCtr!.currentAudioQa =
|
||||
AudioQualityCode.fromCode(quality)!;
|
||||
@ -493,13 +496,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
// 选择解码格式
|
||||
void showSetDecodeFormats() {
|
||||
// 当前选中的解码格式
|
||||
VideoDecodeFormats currentDecodeFormats =
|
||||
final VideoDecodeFormats currentDecodeFormats =
|
||||
widget.videoDetailCtr!.currentDecodeFormats;
|
||||
VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
||||
final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
||||
// 当前视频可用的解码格式
|
||||
List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
List list = videoFormat
|
||||
.firstWhere((e) => e.quality == firstVideo.quality!.code)
|
||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
final List list = videoFormat
|
||||
.firstWhere((FormatItem e) => e.quality == firstVideo.quality!.code)
|
||||
.codecs!;
|
||||
|
||||
showModalBottomSheet(
|
||||
@ -565,15 +568,15 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
/// 弹幕功能
|
||||
void showSetDanmaku() async {
|
||||
// 屏蔽类型
|
||||
List<Map<String, dynamic>> blockTypesList = [
|
||||
final List<Map<String, dynamic>> blockTypesList = [
|
||||
{'value': 5, 'label': '顶部'},
|
||||
{'value': 2, 'label': '滚动'},
|
||||
{'value': 4, 'label': '底部'},
|
||||
{'value': 6, 'label': '彩色'},
|
||||
];
|
||||
List blockTypes = widget.controller!.blockTypes;
|
||||
final List blockTypes = widget.controller!.blockTypes;
|
||||
// 显示区域
|
||||
List<Map<String, dynamic>> showAreas = [
|
||||
final List<Map<String, dynamic>> showAreas = [
|
||||
{'value': 0.25, 'label': '1/4屏'},
|
||||
{'value': 0.5, 'label': '半屏'},
|
||||
{'value': 0.75, 'label': '3/4屏'},
|
||||
@ -587,13 +590,15 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
// 弹幕速度
|
||||
double danmakuDurationVal = widget.controller!.danmakuDurationVal;
|
||||
|
||||
DanmakuController danmakuController = widget.controller!.danmakuController!;
|
||||
final DanmakuController danmakuController =
|
||||
widget.controller!.danmakuController!;
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return StatefulBuilder(builder: (context, StateSetter setState) {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 580,
|
||||
@ -617,11 +622,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 18),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var i in blockTypesList) ...[
|
||||
children: <Widget>[
|
||||
for (final Map<String, dynamic> i
|
||||
in blockTypesList) ...<Widget>[
|
||||
ActionRowLineItem(
|
||||
onTap: () async {
|
||||
bool isChoose = blockTypes.contains(i['value']);
|
||||
final bool isChoose =
|
||||
blockTypes.contains(i['value']);
|
||||
if (isChoose) {
|
||||
blockTypes.remove(i['value']);
|
||||
} else {
|
||||
@ -630,9 +637,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
widget.controller!.blockTypes = blockTypes;
|
||||
setState(() {});
|
||||
try {
|
||||
DanmakuOption currentOption =
|
||||
final DanmakuOption currentOption =
|
||||
danmakuController.option;
|
||||
DanmakuOption updatedOption =
|
||||
final DanmakuOption updatedOption =
|
||||
currentOption.copyWith(
|
||||
hideTop: blockTypes.contains(5),
|
||||
hideBottom: blockTypes.contains(4),
|
||||
@ -655,16 +662,16 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 18),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var i in showAreas) ...[
|
||||
for (final Map<String, dynamic> i in showAreas) ...[
|
||||
ActionRowLineItem(
|
||||
onTap: () {
|
||||
showArea = i['value'];
|
||||
widget.controller!.showArea = showArea;
|
||||
setState(() {});
|
||||
try {
|
||||
DanmakuOption currentOption =
|
||||
final DanmakuOption currentOption =
|
||||
danmakuController.option;
|
||||
DanmakuOption updatedOption =
|
||||
final DanmakuOption updatedOption =
|
||||
currentOption.copyWith(area: i['value']);
|
||||
danmakuController.updateOption(updatedOption);
|
||||
} catch (_) {}
|
||||
@ -705,9 +712,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
widget.controller!.opacityVal = opacityVal;
|
||||
setState(() {});
|
||||
try {
|
||||
DanmakuOption currentOption =
|
||||
final DanmakuOption currentOption =
|
||||
danmakuController.option;
|
||||
DanmakuOption updatedOption =
|
||||
final DanmakuOption updatedOption =
|
||||
currentOption.copyWith(opacity: val);
|
||||
danmakuController.updateOption(updatedOption);
|
||||
} catch (_) {}
|
||||
@ -743,9 +750,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
widget.controller!.fontSizeVal = fontSizeVal;
|
||||
setState(() {});
|
||||
try {
|
||||
DanmakuOption currentOption =
|
||||
final DanmakuOption currentOption =
|
||||
danmakuController.option;
|
||||
DanmakuOption updatedOption =
|
||||
final DanmakuOption updatedOption =
|
||||
currentOption.copyWith(
|
||||
fontSize: (15 * fontSizeVal).toDouble(),
|
||||
);
|
||||
@ -780,14 +787,16 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
label: danmakuDurationVal.toString(),
|
||||
onChanged: (double val) {
|
||||
danmakuDurationVal = val;
|
||||
widget.controller!.danmakuDurationVal = danmakuDurationVal;
|
||||
widget.controller!.danmakuDurationVal =
|
||||
danmakuDurationVal;
|
||||
setState(() {});
|
||||
try {
|
||||
DanmakuOption currentOption =
|
||||
final DanmakuOption currentOption =
|
||||
danmakuController.option;
|
||||
DanmakuOption updatedOption =
|
||||
currentOption.copyWith(duration:
|
||||
val/widget.controller!.playbackSpeed);
|
||||
final DanmakuOption updatedOption =
|
||||
currentOption.copyWith(
|
||||
duration:
|
||||
val / widget.controller!.playbackSpeed);
|
||||
danmakuController.updateOption(updatedOption);
|
||||
} catch (_) {}
|
||||
},
|
||||
@ -827,8 +836,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
Expanded(
|
||||
child: Material(
|
||||
child: ListView(
|
||||
children: [
|
||||
for (var i in PlayRepeat.values) ...[
|
||||
children: <Widget>[
|
||||
for (final PlayRepeat i in PlayRepeat.values) ...[
|
||||
ListTile(
|
||||
onTap: () {
|
||||
widget.controller!.setPlayRepeat(i);
|
||||
@ -860,7 +869,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _ = widget.controller!;
|
||||
const textStyle = TextStyle(
|
||||
const TextStyle textStyle = TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
);
|
||||
@ -881,17 +890,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
fuc: () => {
|
||||
if (widget.controller!.isFullScreen.value){
|
||||
widget.controller!.triggerFullScreen(status: false)
|
||||
} else {
|
||||
if (MediaQuery.of(context).orientation == Orientation.landscape){
|
||||
SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
])
|
||||
},
|
||||
Get.back()
|
||||
}
|
||||
fuc: () => <Set<void>>{
|
||||
if (widget.controller!.isFullScreen.value)
|
||||
<void>{widget.controller!.triggerFullScreen(status: false)}
|
||||
else
|
||||
<void>{
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape)
|
||||
{
|
||||
SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
])
|
||||
},
|
||||
Get.back()
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(width: buttonSpace),
|
||||
@ -905,7 +917,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
// 销毁播放器实例
|
||||
await widget.controller!.dispose(type: 'all');
|
||||
if (mounted) {
|
||||
Navigator.popUntil(context, (route) => route.isFirst);
|
||||
Navigator.popUntil(
|
||||
context, (Route<dynamic> route) => route.isFirst);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -954,7 +967,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
),
|
||||
),
|
||||
SizedBox(width: buttonSpace),
|
||||
if (Platform.isAndroid) ...[
|
||||
if (Platform.isAndroid) ...<Widget>[
|
||||
SizedBox(
|
||||
width: 34,
|
||||
height: 34,
|
||||
@ -971,7 +984,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
canUsePiP = false;
|
||||
}
|
||||
if (canUsePiP) {
|
||||
final aspectRatio = Rational(
|
||||
final Rational aspectRatio = Rational(
|
||||
widget.videoDetailCtr!.data.dash!.video!.first.width!,
|
||||
widget.videoDetailCtr!.data.dash!.video!.first.height!,
|
||||
);
|
||||
@ -997,7 +1010,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
),
|
||||
onPressed: () => showSetSpeedSheet(),
|
||||
child: Text(
|
||||
'${_.playbackSpeed.toString()}X',
|
||||
'${_.playbackSpeed}X',
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user