Merge branch 'design'
This commit is contained in:
@ -2,16 +2,17 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
Box<dynamic> setting = GStrorage.setting;
|
||||||
|
|
||||||
class CustomToast extends StatelessWidget {
|
class CustomToast extends StatelessWidget {
|
||||||
|
const CustomToast({super.key, required this.msg});
|
||||||
|
|
||||||
final String msg;
|
final String msg;
|
||||||
const CustomToast({Key? key, required this.msg}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double toastOpacity =
|
final double toastOpacity =
|
||||||
setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0);
|
setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0) as double;
|
||||||
return Container(
|
return Container(
|
||||||
margin:
|
margin:
|
||||||
EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 30),
|
EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 30),
|
||||||
|
|||||||
@ -1,45 +1,46 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'network_img_layer.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class HtmlRender extends StatelessWidget {
|
class HtmlRender extends StatelessWidget {
|
||||||
String? htmlContent;
|
const HtmlRender({
|
||||||
final int? imgCount;
|
|
||||||
final List? imgList;
|
|
||||||
|
|
||||||
HtmlRender({
|
|
||||||
this.htmlContent,
|
this.htmlContent,
|
||||||
this.imgCount,
|
this.imgCount,
|
||||||
this.imgList,
|
this.imgList,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String? htmlContent;
|
||||||
|
final int? imgCount;
|
||||||
|
final List<String>? imgList;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Html(
|
return Html(
|
||||||
data: htmlContent,
|
data: htmlContent,
|
||||||
onLinkTap: (url, buildContext, attributes) => {},
|
onLinkTap: (String? url, Map<String, String> buildContext, attributes) {},
|
||||||
extensions: [
|
extensions: [
|
||||||
TagExtension(
|
TagExtension(
|
||||||
tagsToExtend: {"img"},
|
tagsToExtend: <String>{'img'},
|
||||||
builder: (extensionContext) {
|
builder: (ExtensionContext extensionContext) {
|
||||||
try {
|
try {
|
||||||
Map attributes = extensionContext.attributes;
|
final Map<String, dynamic> attributes =
|
||||||
List key = attributes.keys.toList();
|
extensionContext.attributes;
|
||||||
String? imgUrl = key.contains('src')
|
final List<dynamic> key = attributes.keys.toList();
|
||||||
? attributes['src']
|
String imgUrl = key.contains('src')
|
||||||
: attributes['data-src'];
|
? attributes['src'] as String
|
||||||
if (imgUrl!.startsWith('//')) {
|
: attributes['data-src'] as String;
|
||||||
|
if (imgUrl.startsWith('//')) {
|
||||||
imgUrl = 'https:$imgUrl';
|
imgUrl = 'https:$imgUrl';
|
||||||
}
|
}
|
||||||
if (imgUrl.startsWith('http://')) {
|
if (imgUrl.startsWith('http://')) {
|
||||||
imgUrl = imgUrl.replaceAll('http://', 'https://');
|
imgUrl = imgUrl.replaceAll('http://', 'https://');
|
||||||
}
|
}
|
||||||
imgUrl = imgUrl.contains('@') ? imgUrl.split('@').first : imgUrl;
|
imgUrl = imgUrl.contains('@') ? imgUrl.split('@').first : imgUrl;
|
||||||
bool isEmote = imgUrl.contains('/emote/');
|
final bool isEmote = imgUrl.contains('/emote/');
|
||||||
bool isMall = imgUrl.contains('/mall/');
|
final bool isMall = imgUrl.contains('/mall/');
|
||||||
if (isMall) {
|
if (isMall) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
@ -58,38 +59,37 @@ class HtmlRender extends StatelessWidget {
|
|||||||
src: imgUrl,
|
src: imgUrl,
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
style: {
|
style: {
|
||||||
"html": Style(
|
'html': Style(
|
||||||
fontSize: FontSize.medium,
|
fontSize: FontSize.medium,
|
||||||
lineHeight: LineHeight.percent(140),
|
lineHeight: LineHeight.percent(140),
|
||||||
),
|
),
|
||||||
"body": Style(margin: Margins.zero, padding: HtmlPaddings.zero),
|
'body': Style(margin: Margins.zero, padding: HtmlPaddings.zero),
|
||||||
"a": Style(
|
'a': Style(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
textDecoration: TextDecoration.none,
|
textDecoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
"p": Style(
|
'p': Style(
|
||||||
margin: Margins.only(bottom: 10),
|
margin: Margins.only(bottom: 10),
|
||||||
),
|
),
|
||||||
"span": Style(
|
'span': Style(
|
||||||
fontSize: FontSize.medium,
|
fontSize: FontSize.medium,
|
||||||
height: Height(1.65),
|
height: Height(1.65),
|
||||||
),
|
),
|
||||||
"div": Style(height: Height.auto()),
|
'div': Style(height: Height.auto()),
|
||||||
"li > p": Style(
|
'li > p': Style(
|
||||||
display: Display.inline,
|
display: Display.inline,
|
||||||
),
|
),
|
||||||
"li": Style(
|
'li': Style(
|
||||||
padding: HtmlPaddings.only(bottom: 4),
|
padding: HtmlPaddings.only(bottom: 4),
|
||||||
textAlign: TextAlign.justify,
|
textAlign: TextAlign.justify,
|
||||||
),
|
),
|
||||||
"img": Style(margin: Margins.only(top: 4, bottom: 4)),
|
'img': Style(margin: Margins.only(top: 4, bottom: 4)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../../utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import '../constants.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'network_img_layer.dart';
|
||||||
|
|
||||||
class LiveCard extends StatelessWidget {
|
class LiveCard extends StatelessWidget {
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final liveItem;
|
final dynamic liveItem;
|
||||||
|
|
||||||
const LiveCard({
|
const LiveCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -14,7 +14,7 @@ class LiveCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(liveItem.roomid);
|
final String heroTag = Utils.makeHeroTag(liveItem.roomid);
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
@ -23,7 +23,6 @@ class LiveCard extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(0),
|
borderRadius: BorderRadius.circular(0),
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
||||||
width: 1,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
@ -33,15 +32,16 @@ class LiveCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder:
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
(BuildContext context, BoxConstraints boxConstraints) {
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: liveItem.cover,
|
src: liveItem.cover as String,
|
||||||
type: 'emote',
|
type: 'emote',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
@ -58,7 +58,7 @@ class LiveCard extends StatelessWidget {
|
|||||||
// view: liveItem.stat.view,
|
// view: liveItem.stat.view,
|
||||||
// danmaku: liveItem.stat.danmaku,
|
// danmaku: liveItem.stat.danmaku,
|
||||||
// duration: liveItem.duration,
|
// duration: liveItem.duration,
|
||||||
online: liveItem.online,
|
online: liveItem.online as int,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -90,7 +90,7 @@ class LiveContent extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
liveItem.title,
|
liveItem.title as String,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
@ -99,7 +99,7 @@ class LiveContent extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Text(
|
child: Text(
|
||||||
liveItem.uname,
|
liveItem.uname as String,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
@ -114,9 +114,9 @@ class LiveContent extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LiveStat extends StatelessWidget {
|
class LiveStat extends StatelessWidget {
|
||||||
final int? online;
|
const LiveStat({super.key, required this.online});
|
||||||
|
|
||||||
const LiveStat({Key? key, required this.online}) : super(key: key);
|
final int? online;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -136,7 +136,7 @@ class LiveStat extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Row(
|
// Row(
|
||||||
// children: [
|
// children: [
|
||||||
// StatView(
|
// StatView(
|
||||||
|
|||||||
@ -1,24 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../../utils/storage.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import '../constants.dart';
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
Box<dynamic> setting = GStrorage.setting;
|
||||||
|
|
||||||
class NetworkImgLayer extends StatelessWidget {
|
class NetworkImgLayer extends StatelessWidget {
|
||||||
final String? src;
|
|
||||||
final double? width;
|
|
||||||
final double? height;
|
|
||||||
final double? cacheW;
|
|
||||||
final double? cacheH;
|
|
||||||
final String? type;
|
|
||||||
final Duration? fadeOutDuration;
|
|
||||||
final Duration? fadeInDuration;
|
|
||||||
final int? quality;
|
|
||||||
|
|
||||||
const NetworkImgLayer({
|
const NetworkImgLayer({
|
||||||
Key? key,
|
super.key,
|
||||||
this.src,
|
this.src,
|
||||||
required this.width,
|
required this.width,
|
||||||
required this.height,
|
required this.height,
|
||||||
@ -29,12 +19,23 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
this.fadeInDuration,
|
this.fadeInDuration,
|
||||||
// 图片质量 默认1%
|
// 图片质量 默认1%
|
||||||
this.quality,
|
this.quality,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
|
final String? src;
|
||||||
|
final double? width;
|
||||||
|
final double? height;
|
||||||
|
final double? cacheW;
|
||||||
|
final double? cacheH;
|
||||||
|
final String? type;
|
||||||
|
final Duration? fadeOutDuration;
|
||||||
|
final Duration? fadeInDuration;
|
||||||
|
final int? quality;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double pr = MediaQuery.of(context).devicePixelRatio;
|
final double pr = MediaQuery.of(context).devicePixelRatio;
|
||||||
int picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
|
final int picQuality =
|
||||||
|
setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10) as int;
|
||||||
|
|
||||||
// double pr = 2;
|
// double pr = 2;
|
||||||
return src != ''
|
return src != ''
|
||||||
@ -50,7 +51,6 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? picQuality}q.webp',
|
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? picQuality}q.webp',
|
||||||
width: width ?? double.infinity,
|
width: width ?? double.infinity,
|
||||||
height: height ?? double.infinity,
|
height: height ?? double.infinity,
|
||||||
alignment: Alignment.center,
|
|
||||||
maxWidthDiskCache: ((cacheW ?? width!) * pr).toInt(),
|
maxWidthDiskCache: ((cacheW ?? width!) * pr).toInt(),
|
||||||
// maxHeightDiskCache: (cacheH ?? height!).toInt(),
|
// maxHeightDiskCache: (cacheH ?? height!).toInt(),
|
||||||
memCacheWidth: ((cacheW ?? width!) * pr).toInt(),
|
memCacheWidth: ((cacheW ?? width!) * pr).toInt(),
|
||||||
@ -61,14 +61,16 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
fadeInDuration:
|
fadeInDuration:
|
||||||
fadeInDuration ?? const Duration(milliseconds: 200),
|
fadeInDuration ?? const Duration(milliseconds: 200),
|
||||||
// filterQuality: FilterQuality.high,
|
// filterQuality: FilterQuality.high,
|
||||||
errorWidget: (context, url, error) => placeholder(context),
|
errorWidget: (BuildContext context, String url, Object error) =>
|
||||||
placeholder: (context, url) => placeholder(context),
|
placeholder(context),
|
||||||
|
placeholder: (BuildContext context, String url) =>
|
||||||
|
placeholder(context),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: placeholder(context);
|
: placeholder(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget placeholder(context) {
|
Widget placeholder(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
width: width ?? double.infinity,
|
width: width ?? double.infinity,
|
||||||
height: height ?? double.infinity,
|
height: height ?? double.infinity,
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../../utils/download.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import '../constants.dart';
|
||||||
import 'package:pilipala/utils/download.dart';
|
import 'network_img_layer.dart';
|
||||||
|
|
||||||
class OverlayPop extends StatelessWidget {
|
class OverlayPop extends StatelessWidget {
|
||||||
|
const OverlayPop({super.key, this.videoItem, this.closeFn});
|
||||||
|
|
||||||
final dynamic videoItem;
|
final dynamic videoItem;
|
||||||
final Function? closeFn;
|
final Function? closeFn;
|
||||||
const OverlayPop({super.key, this.videoItem, this.closeFn});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double imgWidth = MediaQuery.of(context).size.width - 8 * 2;
|
final double imgWidth = MediaQuery.sizeOf(context).width - 8 * 2;
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -19,7 +20,6 @@ class OverlayPop extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
Stack(
|
||||||
@ -27,7 +27,7 @@ class OverlayPop extends StatelessWidget {
|
|||||||
NetworkImgLayer(
|
NetworkImgLayer(
|
||||||
width: imgWidth,
|
width: imgWidth,
|
||||||
height: imgWidth / StyleString.aspectRatio,
|
height: imgWidth / StyleString.aspectRatio,
|
||||||
src: videoItem.pic!,
|
src: videoItem.pic! as String,
|
||||||
quality: 100,
|
quality: 100,
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -61,7 +61,7 @@ class OverlayPop extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
videoItem.title!,
|
videoItem.title! as String,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
@ -69,7 +69,10 @@ class OverlayPop extends StatelessWidget {
|
|||||||
tooltip: '保存封面图',
|
tooltip: '保存封面图',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await DownloadUtils.downloadImg(
|
await DownloadUtils.downloadImg(
|
||||||
videoItem.pic ?? videoItem.cover);
|
videoItem.pic != null
|
||||||
|
? videoItem.pic as String
|
||||||
|
: videoItem.cover as String,
|
||||||
|
);
|
||||||
// closeFn!();
|
// closeFn!();
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.download, size: 20),
|
icon: const Icon(Icons.download, size: 20),
|
||||||
|
|||||||
@ -17,8 +17,8 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
this.info,
|
this.info,
|
||||||
this.lastRefreshTime, {
|
this.lastRefreshTime, {
|
||||||
this.color,
|
this.color,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final PullToRefreshScrollNotificationInfo? info;
|
final PullToRefreshScrollNotificationInfo? info;
|
||||||
final DateTime? lastRefreshTime;
|
final DateTime? lastRefreshTime;
|
||||||
@ -28,7 +28,7 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final PullToRefreshScrollNotificationInfo? infos = info;
|
final PullToRefreshScrollNotificationInfo? infos = info;
|
||||||
if (infos == null) {
|
if (infos == null) {
|
||||||
return Container();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
String text = '';
|
String text = '';
|
||||||
if (infos.mode == PullToRefreshIndicatorMode.armed) {
|
if (infos.mode == PullToRefreshIndicatorMode.armed) {
|
||||||
@ -65,7 +65,6 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
top: top,
|
top: top,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
@ -1,17 +1,28 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import '../../http/search.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../../http/user.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import '../../utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
import '../constants.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'badge.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'network_img_layer.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'stat/danmu.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'stat/view.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
|
|
||||||
// 视频卡片 - 水平布局
|
// 视频卡片 - 水平布局
|
||||||
class VideoCardH extends StatelessWidget {
|
class VideoCardH extends StatelessWidget {
|
||||||
|
const VideoCardH({
|
||||||
|
super.key,
|
||||||
|
required this.videoItem,
|
||||||
|
this.longPress,
|
||||||
|
this.longPressEnd,
|
||||||
|
this.source = 'normal',
|
||||||
|
this.showOwner = true,
|
||||||
|
this.showView = true,
|
||||||
|
this.showDanmaku = true,
|
||||||
|
this.showPubdate = false,
|
||||||
|
});
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final videoItem;
|
final videoItem;
|
||||||
final Function()? longPress;
|
final Function()? longPress;
|
||||||
@ -22,23 +33,11 @@ class VideoCardH extends StatelessWidget {
|
|||||||
final bool showDanmaku;
|
final bool showDanmaku;
|
||||||
final bool showPubdate;
|
final bool showPubdate;
|
||||||
|
|
||||||
const VideoCardH({
|
|
||||||
Key? key,
|
|
||||||
required this.videoItem,
|
|
||||||
this.longPress,
|
|
||||||
this.longPressEnd,
|
|
||||||
this.source = 'normal',
|
|
||||||
this.showOwner = true,
|
|
||||||
this.showView = true,
|
|
||||||
this.showDanmaku = true,
|
|
||||||
this.showPubdate = false,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
int aid = videoItem.aid;
|
final int aid = videoItem.aid;
|
||||||
String bvid = videoItem.bvid;
|
final String bvid = videoItem.bvid;
|
||||||
String heroTag = Utils.makeHeroTag(aid);
|
final String heroTag = Utils.makeHeroTag(aid);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (longPress != null) {
|
if (longPress != null) {
|
||||||
@ -53,7 +52,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
try {
|
try {
|
||||||
int cid =
|
final int cid =
|
||||||
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||||
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
||||||
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
|
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
|
||||||
@ -65,8 +64,8 @@ class VideoCardH extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
||||||
double width = (boxConstraints.maxWidth -
|
final double width = (boxConstraints.maxWidth -
|
||||||
StyleString.cardSpace *
|
StyleString.cardSpace *
|
||||||
6 /
|
6 /
|
||||||
MediaQuery.of(context).textScaleFactor) /
|
MediaQuery.of(context).textScaleFactor) /
|
||||||
@ -77,29 +76,28 @@ class VideoCardH extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (BuildContext context,
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
BoxConstraints boxConstraints) {
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: videoItem.pic,
|
src: videoItem.pic as String,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PBadge(
|
PBadge(
|
||||||
text: Utils.timeFormat(videoItem.duration!),
|
text: Utils.timeFormat(videoItem.duration!),
|
||||||
top: null,
|
|
||||||
right: 6.0,
|
right: 6.0,
|
||||||
bottom: 6.0,
|
bottom: 6.0,
|
||||||
left: null,
|
|
||||||
type: 'gray',
|
type: 'gray',
|
||||||
),
|
),
|
||||||
// if (videoItem.rcmdReason != null &&
|
// if (videoItem.rcmdReason != null &&
|
||||||
@ -159,7 +157,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (videoItem.title is String) ...[
|
if (videoItem.title is String) ...[
|
||||||
Text(
|
Text(
|
||||||
videoItem.title,
|
videoItem.title as String,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
@ -172,9 +170,9 @@ class VideoContent extends StatelessWidget {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: [
|
children: [
|
||||||
for (var i in videoItem.title) ...[
|
for (final i in videoItem.title) ...[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: i['text'],
|
text: i['text'] as String,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0.3,
|
letterSpacing: 0.3,
|
||||||
@ -216,7 +214,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
videoItem.owner.name,
|
videoItem.owner.name as String,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize:
|
fontSize:
|
||||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
@ -230,14 +228,14 @@ class VideoContent extends StatelessWidget {
|
|||||||
if (showView) ...[
|
if (showView) ...[
|
||||||
StatView(
|
StatView(
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
view: videoItem.stat.view,
|
view: videoItem.stat.view as int,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
if (showDanmaku)
|
if (showDanmaku)
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
danmu: videoItem.stat.danmaku,
|
danmu: videoItem.stat.danmaku as int,
|
||||||
),
|
),
|
||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
@ -281,8 +279,8 @@ class VideoContent extends StatelessWidget {
|
|||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
var res = await UserHttp.toViewLater(
|
var res = await UserHttp.toViewLater(
|
||||||
bvid: videoItem.bvid);
|
bvid: videoItem.bvid as String);
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg'] as String);
|
||||||
},
|
},
|
||||||
value: 'pause',
|
value: 'pause',
|
||||||
height: 35,
|
height: 35,
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import '../../http/dynamics.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../../http/search.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import '../../http/user.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
import '../../models/common/search_type.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import '../../utils/id_utils.dart';
|
||||||
import 'package:pilipala/http/dynamics.dart';
|
import '../../utils/utils.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import '../constants.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'badge.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'network_img_layer.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
|
||||||
import 'package:pilipala/utils/utils.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
|
|
||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class VideoCardV extends StatelessWidget {
|
class VideoCardV extends StatelessWidget {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:pilipala/http/index.dart';
|
import '../models/bangumi/list.dart';
|
||||||
import 'package:pilipala/models/bangumi/list.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class BangumiHttp {
|
class BangumiHttp {
|
||||||
static Future bangumiList({int? page}) async {
|
static Future bangumiList({int? page}) async {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:pilipala/http/index.dart';
|
import '../models/user/black.dart';
|
||||||
import 'package:pilipala/models/user/black.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class BlackHttp {
|
class BlackHttp {
|
||||||
static Future blackList({required int pn, int? ps}) async {
|
static Future blackList({required int pn, int? ps}) async {
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import '../models/danmaku/dm.pb.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
import 'index.dart';
|
||||||
import 'package:pilipala/models/danmaku/dm.pb.dart';
|
|
||||||
|
|
||||||
import 'constants.dart';
|
|
||||||
|
|
||||||
class DanmakaHttp {
|
class DanmakaHttp {
|
||||||
// 获取视频弹幕
|
// 获取视频弹幕
|
||||||
@ -24,11 +21,13 @@ class DanmakaHttp {
|
|||||||
);
|
);
|
||||||
return DmSegMobileReply.fromBuffer(response.data);
|
return DmSegMobileReply.fromBuffer(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future shootDanmaku({
|
static Future shootDanmaku({
|
||||||
int type = 1, //弹幕类选择(1:视频弹幕 2:漫画弹幕)
|
int type = 1, //弹幕类选择(1:视频弹幕 2:漫画弹幕)
|
||||||
required int oid, // 视频cid
|
required int oid, // 视频cid
|
||||||
required String msg, //弹幕文本(长度小于 100 字符)
|
required String msg, //弹幕文本(长度小于 100 字符)
|
||||||
int mode = 1,// 弹幕类型(1:滚动弹幕 4:底端弹幕 5:顶端弹幕 6:逆向弹幕(不能使用) 7:高级弹幕 8:代码弹幕(不能使用) 9:BAS弹幕(pool必须为2))
|
int mode =
|
||||||
|
1, // 弹幕类型(1:滚动弹幕 4:底端弹幕 5:顶端弹幕 6:逆向弹幕(不能使用) 7:高级弹幕 8:代码弹幕(不能使用) 9:BAS弹幕(pool必须为2))
|
||||||
// String? aid,// 稿件avid
|
// String? aid,// 稿件avid
|
||||||
// String? bvid,// bvid与aid必须有一个
|
// String? bvid,// bvid与aid必须有一个
|
||||||
required String bvid,
|
required String bvid,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:pilipala/http/index.dart';
|
import '../models/dynamics/result.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import '../models/dynamics/up.dart';
|
||||||
import 'package:pilipala/models/dynamics/up.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class DynamicsHttp {
|
class DynamicsHttp {
|
||||||
static Future followDynamic({
|
static Future followDynamic({
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:pilipala/http/index.dart';
|
import '../models/fans/result.dart';
|
||||||
import 'package:pilipala/models/fans/result.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class FanHttp {
|
class FanHttp {
|
||||||
static Future fans({int? vmid, int? pn, int? ps, String? orderType}) async {
|
static Future fans({int? vmid, int? pn, int? ps, String? orderType}) async {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:pilipala/http/index.dart';
|
import '../models/follow/result.dart';
|
||||||
import 'package:pilipala/models/follow/result.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class FollowHttp {
|
class FollowHttp {
|
||||||
static Future followings(
|
static Future followings(
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:html/dom.dart';
|
import 'package:html/dom.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class HtmlHttp {
|
class HtmlHttp {
|
||||||
// article
|
// article
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:cookie_jar/cookie_jar.dart';
|
import 'package:cookie_jar/cookie_jar.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import '../utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import '../utils/utils.dart';
|
||||||
import 'package:pilipala/http/constants.dart';
|
import 'constants.dart';
|
||||||
import 'package:pilipala/http/interceptor.dart';
|
import 'interceptor.dart';
|
||||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
static final Request _instance = Request._internal();
|
static final Request _instance = Request._internal();
|
||||||
@ -20,25 +20,25 @@ class Request {
|
|||||||
factory Request() => _instance;
|
factory Request() => _instance;
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
static Box localCache = GStrorage.localCache;
|
static Box localCache = GStrorage.localCache;
|
||||||
late dynamic enableSystemProxy;
|
late bool enableSystemProxy;
|
||||||
late String systemProxyHost;
|
late String systemProxyHost;
|
||||||
late String systemProxyPort;
|
late String systemProxyPort;
|
||||||
|
|
||||||
/// 设置cookie
|
/// 设置cookie
|
||||||
static setCookie() async {
|
static setCookie() async {
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
var cookiePath = await Utils.getCookiePath();
|
final String cookiePath = await Utils.getCookiePath();
|
||||||
var cookieJar = PersistCookieJar(
|
final PersistCookieJar cookieJar = PersistCookieJar(
|
||||||
ignoreExpires: true,
|
ignoreExpires: true,
|
||||||
storage: FileStorage(cookiePath),
|
storage: FileStorage(cookiePath),
|
||||||
);
|
);
|
||||||
cookieManager = CookieManager(cookieJar);
|
cookieManager = CookieManager(cookieJar);
|
||||||
dio.interceptors.add(cookieManager);
|
dio.interceptors.add(cookieManager);
|
||||||
var cookie = await cookieManager.cookieJar
|
final List<Cookie> cookie = await cookieManager.cookieJar
|
||||||
.loadForRequest(Uri.parse(HttpString.baseUrl));
|
.loadForRequest(Uri.parse(HttpString.baseUrl));
|
||||||
var userInfo = userInfoCache.get('userInfoCache');
|
final userInfo = userInfoCache.get('userInfoCache');
|
||||||
if (userInfo != null && userInfo.mid != null) {
|
if (userInfo != null && userInfo.mid != null) {
|
||||||
var cookie2 = await cookieManager.cookieJar
|
final List<Cookie> cookie2 = await cookieManager.cookieJar
|
||||||
.loadForRequest(Uri.parse(HttpString.tUrl));
|
.loadForRequest(Uri.parse(HttpString.tUrl));
|
||||||
if (cookie2.isEmpty) {
|
if (cookie2.isEmpty) {
|
||||||
try {
|
try {
|
||||||
@ -57,14 +57,15 @@ class Request {
|
|||||||
log("setCookie, ${e.toString()}");
|
log("setCookie, ${e.toString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var cookieString =
|
final String cookieString = cookie
|
||||||
cookie.map((cookie) => '${cookie.name}=${cookie.value}').join('; ');
|
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
|
||||||
|
.join('; ');
|
||||||
dio.options.headers['cookie'] = cookieString;
|
dio.options.headers['cookie'] = cookieString;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从cookie中获取 csrf token
|
// 从cookie中获取 csrf token
|
||||||
static Future<String> getCsrf() async {
|
static Future<String> getCsrf() async {
|
||||||
var cookies = await cookieManager.cookieJar
|
List<Cookie> cookies = await cookieManager.cookieJar
|
||||||
.loadForRequest(Uri.parse(HttpString.apiBaseUrl));
|
.loadForRequest(Uri.parse(HttpString.apiBaseUrl));
|
||||||
String token = '';
|
String token = '';
|
||||||
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) {
|
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) {
|
||||||
@ -73,7 +74,7 @@ class Request {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static setOptionsHeaders(userInfo, status) {
|
static setOptionsHeaders(userInfo, bool status) {
|
||||||
if (status) {
|
if (status) {
|
||||||
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
||||||
}
|
}
|
||||||
@ -100,8 +101,8 @@ class Request {
|
|||||||
headers: {},
|
headers: {},
|
||||||
);
|
);
|
||||||
|
|
||||||
enableSystemProxy =
|
enableSystemProxy = setting.get(SettingBoxKey.enableSystemProxy,
|
||||||
setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false);
|
defaultValue: false) as bool;
|
||||||
systemProxyHost =
|
systemProxyHost =
|
||||||
localCache.get(LocalCacheKey.systemProxyHost, defaultValue: '');
|
localCache.get(LocalCacheKey.systemProxyHost, defaultValue: '');
|
||||||
systemProxyPort =
|
systemProxyPort =
|
||||||
@ -113,7 +114,8 @@ class Request {
|
|||||||
..httpClientAdapter = Http2Adapter(
|
..httpClientAdapter = Http2Adapter(
|
||||||
ConnectionManager(
|
ConnectionManager(
|
||||||
idleTimeout: const Duration(milliseconds: 10000),
|
idleTimeout: const Duration(milliseconds: 10000),
|
||||||
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
|
onClientCreate: (_, ClientSetting config) =>
|
||||||
|
config.onBadCertificate = (_) => true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -121,9 +123,9 @@ class Request {
|
|||||||
if (enableSystemProxy) {
|
if (enableSystemProxy) {
|
||||||
dio.httpClientAdapter = IOHttpClientAdapter(
|
dio.httpClientAdapter = IOHttpClientAdapter(
|
||||||
createHttpClient: () {
|
createHttpClient: () {
|
||||||
final client = HttpClient();
|
final HttpClient client = HttpClient();
|
||||||
// Config the client.
|
// Config the client.
|
||||||
client.findProxy = (uri) {
|
client.findProxy = (Uri uri) {
|
||||||
// return 'PROXY host:port';
|
// return 'PROXY host:port';
|
||||||
return 'PROXY $systemProxyHost:$systemProxyPort';
|
return 'PROXY $systemProxyHost:$systemProxyPort';
|
||||||
};
|
};
|
||||||
@ -145,7 +147,7 @@ class Request {
|
|||||||
));
|
));
|
||||||
|
|
||||||
dio.transformer = BackgroundTransformer();
|
dio.transformer = BackgroundTransformer();
|
||||||
dio.options.validateStatus = (status) {
|
dio.options.validateStatus = (int? status) {
|
||||||
return status! >= 200 && status < 300 ||
|
return status! >= 200 && status < 300 ||
|
||||||
HttpString.validateStatusCodes.contains(status);
|
HttpString.validateStatusCodes.contains(status);
|
||||||
};
|
};
|
||||||
@ -156,7 +158,7 @@ class Request {
|
|||||||
*/
|
*/
|
||||||
get(url, {data, options, cancelToken, extra}) async {
|
get(url, {data, options, cancelToken, extra}) async {
|
||||||
Response response;
|
Response response;
|
||||||
Options options = Options();
|
final Options options = Options();
|
||||||
ResponseType resType = ResponseType.json;
|
ResponseType resType = ResponseType.json;
|
||||||
if (extra != null) {
|
if (extra != null) {
|
||||||
resType = extra!['resType'] ?? ResponseType.json;
|
resType = extra!['resType'] ?? ResponseType.json;
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import '../utils/storage.dart';
|
||||||
// import 'package:get/get.dart' hide Response;
|
// import 'package:get/get.dart' hide Response;
|
||||||
|
|
||||||
class ApiInterceptor extends Interceptor {
|
class ApiInterceptor extends Interceptor {
|
||||||
@ -21,16 +21,16 @@ class ApiInterceptor extends Interceptor {
|
|||||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||||
try {
|
try {
|
||||||
if (response.statusCode == 302) {
|
if (response.statusCode == 302) {
|
||||||
List<String> locations = response.headers['location']!;
|
final List<String> locations = response.headers['location']!;
|
||||||
if (locations.isNotEmpty) {
|
if (locations.isNotEmpty) {
|
||||||
if (locations.first.startsWith('https://www.mcbbs.net')) {
|
if (locations.first.startsWith('https://www.mcbbs.net')) {
|
||||||
final uri = Uri.parse(locations.first);
|
final Uri uri = Uri.parse(locations.first);
|
||||||
final accessKey = uri.queryParameters['access_key'];
|
final String? accessKey = uri.queryParameters['access_key'];
|
||||||
final mid = uri.queryParameters['mid'];
|
final String? mid = uri.queryParameters['mid'];
|
||||||
try {
|
try {
|
||||||
Box localCache = GStrorage.localCache;
|
Box localCache = GStrorage.localCache;
|
||||||
localCache.put(
|
localCache.put(LocalCacheKey.accessKey,
|
||||||
LocalCacheKey.accessKey, {'mid': mid, 'value': accessKey});
|
<String, String?>{'mid': mid, 'value': accessKey});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,47 +53,53 @@ class ApiInterceptor extends Interceptor {
|
|||||||
super.onError(err, handler);
|
super.onError(err, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future dioError(DioException error) async {
|
static Future<String> dioError(DioException error) async {
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case DioExceptionType.badCertificate:
|
case DioExceptionType.badCertificate:
|
||||||
return '证书有误!';
|
return '证书有误!';
|
||||||
case DioExceptionType.badResponse:
|
case DioExceptionType.badResponse:
|
||||||
return '服务器异常,请稍后重试!';
|
return '服务器异常,请稍后重试!';
|
||||||
case DioExceptionType.cancel:
|
case DioExceptionType.cancel:
|
||||||
return "请求已被取消,请重新请求";
|
return '请求已被取消,请重新请求';
|
||||||
case DioExceptionType.connectionError:
|
case DioExceptionType.connectionError:
|
||||||
return '连接错误,请检查网络设置';
|
return '连接错误,请检查网络设置';
|
||||||
case DioExceptionType.connectionTimeout:
|
case DioExceptionType.connectionTimeout:
|
||||||
return "网络连接超时,请检查网络设置";
|
return '网络连接超时,请检查网络设置';
|
||||||
case DioExceptionType.receiveTimeout:
|
case DioExceptionType.receiveTimeout:
|
||||||
return "响应超时,请稍后重试!";
|
return '响应超时,请稍后重试!';
|
||||||
case DioExceptionType.sendTimeout:
|
case DioExceptionType.sendTimeout:
|
||||||
return "发送请求超时,请检查网络设置";
|
return '发送请求超时,请检查网络设置';
|
||||||
case DioExceptionType.unknown:
|
case DioExceptionType.unknown:
|
||||||
var res = await checkConect();
|
final String res = await checkConect();
|
||||||
return res + " \n 网络异常,请稍后重试!";
|
return '$res \n 网络异常,请稍后重试!';
|
||||||
default:
|
// default:
|
||||||
return "Dio异常";
|
// return 'Dio异常';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<dynamic> checkConect() async {
|
static Future<String> checkConect() async {
|
||||||
final connectivityResult = await (Connectivity().checkConnectivity());
|
final ConnectivityResult connectivityResult =
|
||||||
|
await Connectivity().checkConnectivity();
|
||||||
if (connectivityResult == ConnectivityResult.mobile) {
|
if (connectivityResult == ConnectivityResult.mobile) {
|
||||||
return 'connected with mobile network';
|
return 'connected with mobile network';
|
||||||
} else if (connectivityResult == ConnectivityResult.wifi) {
|
} else if (connectivityResult == ConnectivityResult.wifi) {
|
||||||
return 'connected with wifi network';
|
return 'connected with wifi network';
|
||||||
} else if (connectivityResult == ConnectivityResult.ethernet) {
|
} else if (connectivityResult == ConnectivityResult.ethernet) {
|
||||||
// I am connected to a ethernet network.
|
// I am connected to a ethernet network.
|
||||||
|
return '';
|
||||||
} else if (connectivityResult == ConnectivityResult.vpn) {
|
} else if (connectivityResult == ConnectivityResult.vpn) {
|
||||||
// I am connected to a vpn network.
|
// I am connected to a vpn network.
|
||||||
// Note for iOS and macOS:
|
// Note for iOS and macOS:
|
||||||
// There is no separate network interface type for [vpn].
|
// There is no separate network interface type for [vpn].
|
||||||
// It returns [other] on any device (also simulator)
|
// It returns [other] on any device (also simulator)
|
||||||
|
return '';
|
||||||
} else if (connectivityResult == ConnectivityResult.other) {
|
} else if (connectivityResult == ConnectivityResult.other) {
|
||||||
// I am connected to a network which is not in the above mentioned networks.
|
// I am connected to a network which is not in the above mentioned networks.
|
||||||
|
return '';
|
||||||
} else if (connectivityResult == ConnectivityResult.none) {
|
} else if (connectivityResult == ConnectivityResult.none) {
|
||||||
return 'not connected to any network';
|
return 'not connected to any network';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import 'package:pilipala/http/api.dart';
|
import '../models/live/item.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import '../models/live/room_info.dart';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'api.dart';
|
||||||
import 'package:pilipala/models/live/room_info.dart';
|
import 'init.dart';
|
||||||
|
|
||||||
class LiveHttp {
|
class LiveHttp {
|
||||||
static Future liveList(
|
static Future liveList(
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:encrypt/encrypt.dart';
|
import 'package:encrypt/encrypt.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
|
||||||
import 'package:pilipala/models/login/index.dart';
|
|
||||||
import 'package:pilipala/utils/login.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
import '../models/login/index.dart';
|
||||||
|
import '../utils/login.dart';
|
||||||
|
import 'index.dart';
|
||||||
|
|
||||||
class LoginHttp {
|
class LoginHttp {
|
||||||
static Future queryCaptcha() async {
|
static Future queryCaptcha() async {
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../common/constants.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
import '../models/dynamics/result.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import '../models/follow/result.dart';
|
||||||
import 'package:pilipala/models/follow/result.dart';
|
import '../models/member/archive.dart';
|
||||||
import 'package:pilipala/models/member/archive.dart';
|
import '../models/member/coin.dart';
|
||||||
import 'package:pilipala/models/member/coin.dart';
|
import '../models/member/info.dart';
|
||||||
import 'package:pilipala/models/member/info.dart';
|
import '../models/member/seasons.dart';
|
||||||
import 'package:pilipala/models/member/seasons.dart';
|
import '../models/member/tags.dart';
|
||||||
import 'package:pilipala/models/member/tags.dart';
|
import '../utils/storage.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import '../utils/utils.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import '../utils/wbi_sign.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class MemberHttp {
|
class MemberHttp {
|
||||||
static Future memberInfo({
|
static Future memberInfo({
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import 'package:pilipala/http/api.dart';
|
import '../models/msg/account.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import '../models/msg/session.dart';
|
||||||
import 'package:pilipala/models/msg/account.dart';
|
import '../utils/wbi_sign.dart';
|
||||||
import 'package:pilipala/models/msg/session.dart';
|
import 'api.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
import 'init.dart';
|
||||||
|
|
||||||
class MsgHttp {
|
class MsgHttp {
|
||||||
// 会话列表
|
// 会话列表
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:pilipala/http/api.dart';
|
import '../models/video/reply/data.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import 'api.dart';
|
||||||
import 'package:pilipala/models/video/reply/data.dart';
|
import 'init.dart';
|
||||||
|
|
||||||
class ReplyHttp {
|
class ReplyHttp {
|
||||||
static Future replyList({
|
static Future replyList({
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
import '../models/bangumi/info.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import '../models/common/search_type.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import '../models/search/hot.dart';
|
||||||
import 'package:pilipala/models/search/hot.dart';
|
import '../models/search/result.dart';
|
||||||
import 'package:pilipala/models/search/result.dart';
|
import '../models/search/suggest.dart';
|
||||||
import 'package:pilipala/models/search/suggest.dart';
|
import '../utils/storage.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class SearchHttp {
|
class SearchHttp {
|
||||||
static Box setting = GStrorage.setting;
|
static Box setting = GStrorage.setting;
|
||||||
@ -129,25 +128,28 @@ class SearchHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future ab2c({int? aid, String? bvid}) async {
|
static Future<int> ab2c({int? aid, String? bvid}) async {
|
||||||
Map<String, dynamic> data = {};
|
Map<String, dynamic> data = {};
|
||||||
if (aid != null) {
|
if (aid != null) {
|
||||||
data['aid'] = aid;
|
data['aid'] = aid;
|
||||||
} else if (bvid != null) {
|
} else if (bvid != null) {
|
||||||
data['bvid'] = bvid;
|
data['bvid'] = bvid;
|
||||||
}
|
}
|
||||||
var res = await Request().get(Api.ab2c, data: {...data});
|
final dynamic res =
|
||||||
|
await Request().get(Api.ab2c, data: <String, dynamic>{...data});
|
||||||
return res.data['data'].first['cid'];
|
return res.data['data'].first['cid'];
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future bangumiInfo({int? seasonId, int? epId}) async {
|
static Future<Map<String, dynamic>> bangumiInfo(
|
||||||
Map<String, dynamic> data = {};
|
{int? seasonId, int? epId}) async {
|
||||||
|
final Map<String, dynamic> data = {};
|
||||||
if (seasonId != null) {
|
if (seasonId != null) {
|
||||||
data['season_id'] = seasonId;
|
data['season_id'] = seasonId;
|
||||||
} else if (epId != null) {
|
} else if (epId != null) {
|
||||||
data['ep_id'] = epId;
|
data['ep_id'] = epId;
|
||||||
}
|
}
|
||||||
var res = await Request().get(Api.bangumiInfo, data: {...data});
|
final dynamic res =
|
||||||
|
await Request().get(Api.bangumiInfo, data: <String, dynamic>{...data});
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
'status': true,
|
'status': true,
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../common/constants.dart';
|
||||||
import 'package:pilipala/http/api.dart';
|
import '../models/model_hot_video_item.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import '../models/user/fav_detail.dart';
|
||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import '../models/user/fav_folder.dart';
|
||||||
import 'package:pilipala/models/user/fav_detail.dart';
|
import '../models/user/history.dart';
|
||||||
import 'package:pilipala/models/user/fav_folder.dart';
|
import '../models/user/info.dart';
|
||||||
import 'package:pilipala/models/user/history.dart';
|
import '../models/user/stat.dart';
|
||||||
import 'package:pilipala/models/user/info.dart';
|
import 'api.dart';
|
||||||
import 'package:pilipala/models/user/stat.dart';
|
import 'init.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
|
||||||
|
|
||||||
class UserHttp {
|
class UserHttp {
|
||||||
static Future<dynamic> userStat({required int mid}) async {
|
static Future<dynamic> userStat({required int mid}) async {
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import '../common/constants.dart';
|
||||||
import 'package:pilipala/http/api.dart';
|
import '../models/common/reply_type.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import '../models/home/rcmd/result.dart';
|
||||||
import 'package:pilipala/models/common/reply_type.dart';
|
import '../models/model_hot_video_item.dart';
|
||||||
import 'package:pilipala/models/home/rcmd/result.dart';
|
import '../models/model_rec_video_item.dart';
|
||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import '../models/user/fav_folder.dart';
|
||||||
import 'package:pilipala/models/model_rec_video_item.dart';
|
import '../models/video/ai.dart';
|
||||||
import 'package:pilipala/models/user/fav_folder.dart';
|
import '../models/video/play/url.dart';
|
||||||
import 'package:pilipala/models/video/ai.dart';
|
import '../models/video_detail_res.dart';
|
||||||
import 'package:pilipala/models/video/play/url.dart';
|
import '../utils/storage.dart';
|
||||||
import 'package:pilipala/models/video_detail_res.dart';
|
import '../utils/wbi_sign.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'api.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
import 'init.dart';
|
||||||
|
|
||||||
/// res.data['code'] == 0 请求正常返回结果
|
/// res.data['code'] == 0 请求正常返回结果
|
||||||
/// res.data['data'] 为结果
|
/// res.data['data'] 为结果
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color outline = Theme.of(context).colorScheme.outline;
|
final Color outline = Theme.of(context).colorScheme.outline;
|
||||||
TextStyle subTitleStyle =
|
TextStyle subTitleStyle =
|
||||||
TextStyle(fontSize: 13, color: Theme.of(context).colorScheme.outline);
|
TextStyle(fontSize: 13, color: Theme.of(context).colorScheme.outline);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -29,7 +29,6 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/logo/logo_android_2.png',
|
'assets/images/logo/logo_android_2.png',
|
||||||
@ -47,7 +46,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Obx(
|
Obx(
|
||||||
() => ListTile(
|
() => ListTile(
|
||||||
title: const Text("当前版本"),
|
title: const Text('当前版本'),
|
||||||
trailing: Text(_aboutController.currentVersion.value,
|
trailing: Text(_aboutController.currentVersion.value,
|
||||||
style: subTitleStyle),
|
style: subTitleStyle),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -63,8 +63,8 @@ class BangumiIntroController extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
if (Get.arguments.isNotEmpty) {
|
if (Get.arguments.isNotEmpty as bool) {
|
||||||
if (Get.arguments.containsKey('bangumiItem')) {
|
if (Get.arguments.containsKey('bangumiItem') as bool) {
|
||||||
preRender = true;
|
preRender = true;
|
||||||
bangumiItem = Get.arguments['bangumiItem'];
|
bangumiItem = Get.arguments['bangumiItem'];
|
||||||
// bangumiItem!['pic'] = args.pic;
|
// bangumiItem!['pic'] = args.pic;
|
||||||
|
|||||||
@ -51,12 +51,11 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
cid = widget.cid!;
|
cid = widget.cid!;
|
||||||
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
|
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
|
||||||
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
bangumiIntroController.bangumiDetail.listen((value) {
|
bangumiIntroController.bangumiDetail.listen((BangumiInfoModel value) {
|
||||||
bangumiDetail = value;
|
bangumiDetail = value;
|
||||||
});
|
});
|
||||||
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
|
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
|
||||||
videoDetailCtr.cid.listen((p0) {
|
videoDetailCtr.cid.listen((int p0) {
|
||||||
print('🐶🐶$p0');
|
|
||||||
cid = p0;
|
cid = p0;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
@ -67,7 +66,7 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data['status']) {
|
if (snapshot.data['status']) {
|
||||||
// 请求成功
|
// 请求成功
|
||||||
@ -83,7 +82,7 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
// errMsg: snapshot.data['msg'],
|
// errMsg: snapshot.data['msg'],
|
||||||
// fn: () => Get.back(),
|
// fn: () => Get.back(),
|
||||||
// );
|
// );
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return BangumiInfo(
|
return BangumiInfo(
|
||||||
@ -98,16 +97,16 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BangumiInfo extends StatefulWidget {
|
class BangumiInfo extends StatefulWidget {
|
||||||
final bool loadingStatus;
|
|
||||||
final BangumiInfoModel? bangumiDetail;
|
|
||||||
final int? cid;
|
|
||||||
|
|
||||||
const BangumiInfo({
|
const BangumiInfo({
|
||||||
Key? key,
|
super.key,
|
||||||
this.loadingStatus = false,
|
this.loadingStatus = false,
|
||||||
this.bangumiDetail,
|
this.bangumiDetail,
|
||||||
this.cid,
|
this.cid,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
|
final bool loadingStatus;
|
||||||
|
final BangumiInfoModel? bangumiDetail;
|
||||||
|
final int? cid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BangumiInfo> createState() => _BangumiInfoState();
|
State<BangumiInfo> createState() => _BangumiInfoState();
|
||||||
@ -123,12 +122,15 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
int? cid;
|
int? cid;
|
||||||
bool isProcessing = false;
|
bool isProcessing = false;
|
||||||
void Function()? handleState(Future Function() action) {
|
void Function()? handleState(Future Function() action) {
|
||||||
return isProcessing ? null : () async {
|
return isProcessing
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
setState(() => isProcessing = true);
|
setState(() => isProcessing = true);
|
||||||
await action();
|
await action();
|
||||||
setState(() => isProcessing = false);
|
setState(() => isProcessing = false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -155,7 +157,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return FavPanel(ctr: bangumiIntroController);
|
return FavPanel(ctr: bangumiIntroController);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -175,7 +177,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ThemeData t = Theme.of(context);
|
final ThemeData t = Theme.of(context);
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 20),
|
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 20),
|
||||||
@ -185,7 +187,6 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
Stack(
|
||||||
@ -244,7 +245,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
EdgeInsets.zero),
|
EdgeInsets.zero),
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStateProperty.resolveWith(
|
MaterialStateProperty.resolveWith(
|
||||||
(states) {
|
(Set<MaterialState> states) {
|
||||||
return t
|
return t
|
||||||
.colorScheme.primaryContainer
|
.colorScheme.primaryContainer
|
||||||
.withOpacity(0.7);
|
.withOpacity(0.7);
|
||||||
@ -386,7 +387,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget actionGrid(BuildContext context, bangumiIntroController) {
|
Widget actionGrid(BuildContext context, bangumiIntroController) {
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return Material(
|
return Material(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 16, bottom: 8),
|
padding: const EdgeInsets.only(top: 16, bottom: 8),
|
||||||
@ -394,7 +396,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
height: constraints.maxWidth / 5 * 0.8,
|
height: constraints.maxWidth / 5 * 0.8,
|
||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
primary: false,
|
primary: false,
|
||||||
padding: const EdgeInsets.all(0),
|
padding: EdgeInsets.zero,
|
||||||
crossAxisCount: 5,
|
crossAxisCount: 5,
|
||||||
childAspectRatio: 1.25,
|
childAspectRatio: 1.25,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -402,7 +404,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||||
onTap: handleState(bangumiIntroController.actionLikeVideo),
|
onTap:
|
||||||
|
handleState(bangumiIntroController.actionLikeVideo),
|
||||||
selectStatus: bangumiIntroController.hasLike.value,
|
selectStatus: bangumiIntroController.hasLike.value,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
@ -413,7 +416,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.b),
|
icon: const Icon(FontAwesomeIcons.b),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||||
onTap: handleState(bangumiIntroController.actionCoinVideo),
|
onTap:
|
||||||
|
handleState(bangumiIntroController.actionCoinVideo),
|
||||||
selectStatus: bangumiIntroController.hasCoin.value,
|
selectStatus: bangumiIntroController.hasCoin.value,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:easy_debounce/easy_throttle.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:nil/nil.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
@ -74,7 +75,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await _bangumidController.queryBangumiListFeed(type: 'init');
|
await _bangumidController.queryBangumiListFeed();
|
||||||
return _bangumidController.queryBangumiFollow();
|
return _bangumidController.queryBangumiFollow();
|
||||||
},
|
},
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
@ -112,10 +113,11 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 258,
|
height: 268,
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFutureFollow,
|
future: _futureBuilderFutureFollow,
|
||||||
builder: (context, snapshot) {
|
builder:
|
||||||
|
(BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState ==
|
if (snapshot.connectionState ==
|
||||||
ConnectionState.done) {
|
ConnectionState.done) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
@ -156,10 +158,10 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return nil;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return nil;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -188,7 +190,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
StyleString.safeSpace, 0, StyleString.safeSpace, 0),
|
StyleString.safeSpace, 0, StyleString.safeSpace, 0),
|
||||||
sliver: FutureBuilder(
|
sliver: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data as Map;
|
Map data = snapshot.data as Map;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
@ -206,7 +208,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LoadingMore()
|
const LoadingMore()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -228,7 +230,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return bangumiList!.isNotEmpty
|
return bangumiList!.isNotEmpty
|
||||||
? BangumiCardV(bangumiItem: bangumiList[index])
|
? BangumiCardV(bangumiItem: bangumiList[index])
|
||||||
: const SizedBox();
|
: nil;
|
||||||
},
|
},
|
||||||
childCount: bangumiList!.isNotEmpty ? bangumiList!.length : 10,
|
childCount: bangumiList!.isNotEmpty ? bangumiList!.length : 10,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -8,11 +8,6 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
|
||||||
class BangumiPanel extends StatefulWidget {
|
class BangumiPanel extends StatefulWidget {
|
||||||
final List<EpisodeItem> pages;
|
|
||||||
final int? cid;
|
|
||||||
final double? sheetHeight;
|
|
||||||
final Function? changeFuc;
|
|
||||||
|
|
||||||
const BangumiPanel({
|
const BangumiPanel({
|
||||||
super.key,
|
super.key,
|
||||||
required this.pages,
|
required this.pages,
|
||||||
@ -21,6 +16,11 @@ class BangumiPanel extends StatefulWidget {
|
|||||||
this.changeFuc,
|
this.changeFuc,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final List<EpisodeItem> pages;
|
||||||
|
final int? cid;
|
||||||
|
final double? sheetHeight;
|
||||||
|
final Function? changeFuc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BangumiPanel> createState() => _BangumiPanelState();
|
State<BangumiPanel> createState() => _BangumiPanelState();
|
||||||
}
|
}
|
||||||
@ -50,10 +50,10 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
}
|
}
|
||||||
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
|
||||||
videoDetailCtr.cid.listen((p0) {
|
videoDetailCtr.cid.listen((int p0) {
|
||||||
cid = p0;
|
cid = p0;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
|
currentIndex = widget.pages.indexWhere((EpisodeItem e) => e.cid == cid);
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -106,7 +106,8 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
child: Material(
|
child: Material(
|
||||||
child: ScrollablePositionedList.builder(
|
child: ScrollablePositionedList.builder(
|
||||||
itemCount: widget.pages.length,
|
itemCount: widget.pages.length,
|
||||||
itemBuilder: (context, index) => ListTile(
|
itemBuilder: (BuildContext context, int index) =>
|
||||||
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
changeFucCall(widget.pages[index], index);
|
changeFucCall(widget.pages[index], index);
|
||||||
@ -216,7 +217,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: widget.pages.length,
|
itemCount: widget.pages.length,
|
||||||
itemExtent: 150,
|
itemExtent: 150,
|
||||||
itemBuilder: ((context, i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 150,
|
width: 150,
|
||||||
margin: const EdgeInsets.only(right: 10),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
@ -231,14 +232,13 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
vertical: 8, horizontal: 10),
|
vertical: 8, horizontal: 10),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (i == currentIndex) ...[
|
if (i == currentIndex) ...<Widget>[
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/live.png',
|
'assets/images/live.png',
|
||||||
color:
|
color: Theme.of(context).colorScheme.primary,
|
||||||
Theme.of(context).colorScheme.primary,
|
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6)
|
const SizedBox(width: 6)
|
||||||
@ -248,9 +248,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: i == currentIndex
|
color: i == currentIndex
|
||||||
? Theme.of(context)
|
? Theme.of(context).colorScheme.primary
|
||||||
.colorScheme
|
|
||||||
.primary
|
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.onSurface),
|
.onSurface),
|
||||||
@ -272,9 +270,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: i == currentIndex
|
color: i == currentIndex
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context)
|
: Theme.of(context).colorScheme.onSurface),
|
||||||
.colorScheme
|
|
||||||
.onSurface),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -283,7 +279,8 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
})),
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,17 +11,16 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
|
|
||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class BangumiCardV extends StatelessWidget {
|
class BangumiCardV extends StatelessWidget {
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
|
||||||
final bangumiItem;
|
|
||||||
final Function()? longPress;
|
|
||||||
final Function()? longPressEnd;
|
|
||||||
|
|
||||||
const BangumiCardV({
|
const BangumiCardV({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.bangumiItem,
|
required this.bangumiItem,
|
||||||
this.longPress,
|
this.longPress,
|
||||||
this.longPressEnd,
|
this.longPressEnd,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
|
final bangumiItem;
|
||||||
|
final Function()? longPress;
|
||||||
|
final Function()? longPressEnd;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -43,9 +42,9 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
// },
|
// },
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
int seasonId = bangumiItem.seasonId;
|
final int seasonId = bangumiItem.seasonId;
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
SmartDialog.showLoading(msg: '获取中...');
|
||||||
var res = await SearchHttp.bangumiInfo(seasonId: seasonId);
|
final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
|
||||||
SmartDialog.dismiss().then((value) {
|
SmartDialog.dismiss().then((value) {
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
if (res['data'].episodes.isEmpty) {
|
if (res['data'].episodes.isEmpty) {
|
||||||
@ -81,8 +80,8 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 0.65,
|
aspectRatio: 0.65,
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
@ -124,9 +123,9 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BangumiContent extends StatelessWidget {
|
class BangumiContent extends StatelessWidget {
|
||||||
|
const BangumiContent({super.key, required this.bangumiItem});
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final bangumiItem;
|
final bangumiItem;
|
||||||
const BangumiContent({Key? key, required this.bangumiItem}) : super(key: key);
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
|
|||||||
@ -70,7 +70,7 @@ class _BlackListPageState extends State<BlackListPage> {
|
|||||||
onRefresh: () async => await _blackListController.queryBlacklist(),
|
onRefresh: () async => await _blackListController.queryBlacklist(),
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
var data = snapshot.data;
|
var data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import 'package:pilipala/http/danmaku.dart';
|
import 'package:pilipala/http/danmaku.dart';
|
||||||
import 'package:pilipala/models/danmaku/dm.pb.dart';
|
import 'package:pilipala/models/danmaku/dm.pb.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
|
||||||
|
|
||||||
class PlDanmakuController {
|
class PlDanmakuController {
|
||||||
PlDanmakuController(this.cid);
|
PlDanmakuController(this.cid);
|
||||||
@ -11,16 +10,14 @@ class PlDanmakuController {
|
|||||||
|
|
||||||
bool get initiated => requestedSeg.isNotEmpty;
|
bool get initiated => requestedSeg.isNotEmpty;
|
||||||
|
|
||||||
static int SEGMENT_LENGTH = 60 * 6 * 1000;
|
static int segmentLength = 60 * 6 * 1000;
|
||||||
|
|
||||||
void initiate(int videoDuration, int progress) {
|
void initiate(int videoDuration, int progress) {
|
||||||
if (requestedSeg.isEmpty) {
|
if (requestedSeg.isEmpty) {
|
||||||
int segCount = (videoDuration / SEGMENT_LENGTH).ceil();
|
int segCount = (videoDuration / segmentLength).ceil();
|
||||||
requestedSeg = List<bool>.generate(segCount, (index) => false);
|
requestedSeg = List<bool>.generate(segCount, (index) => false);
|
||||||
}
|
}
|
||||||
queryDanmaku(
|
queryDanmaku(calcSegment(progress));
|
||||||
calcSegment(progress)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -29,14 +26,14 @@ class PlDanmakuController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int calcSegment(int progress) {
|
int calcSegment(int progress) {
|
||||||
return progress ~/ SEGMENT_LENGTH;
|
return progress ~/ segmentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
void queryDanmaku(int segmentIndex) async {
|
void queryDanmaku(int segmentIndex) async {
|
||||||
assert(requestedSeg[segmentIndex] == false);
|
assert(requestedSeg[segmentIndex] == false);
|
||||||
requestedSeg[segmentIndex] = true;
|
requestedSeg[segmentIndex] = true;
|
||||||
DmSegMobileReply result =
|
final DmSegMobileReply result = await DanmakaHttp.queryDanmaku(
|
||||||
await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: segmentIndex + 1);
|
cid: cid, segmentIndex: segmentIndex + 1);
|
||||||
if (result.elems.isNotEmpty) {
|
if (result.elems.isNotEmpty) {
|
||||||
for (var element in result.elems) {
|
for (var element in result.elems) {
|
||||||
int pos = element.progress ~/ 100; //每0.1秒存储一次
|
int pos = element.progress ~/ 100; //每0.1秒存储一次
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:easy_debounce/easy_throttle.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -43,15 +42,13 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
enableShowDanmaku =
|
enableShowDanmaku =
|
||||||
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
||||||
_plDanmakuController =
|
_plDanmakuController = PlDanmakuController(widget.cid);
|
||||||
PlDanmakuController(widget.cid);
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
playerController = widget.playerController;
|
playerController = widget.playerController;
|
||||||
if (enableShowDanmaku || playerController.isOpenDanmu.value) {
|
if (enableShowDanmaku || playerController.isOpenDanmu.value) {
|
||||||
_plDanmakuController.initiate(
|
_plDanmakuController.initiate(
|
||||||
playerController.duration.value.inMilliseconds,
|
playerController.duration.value.inMilliseconds,
|
||||||
playerController.position.value.inMilliseconds
|
playerController.position.value.inMilliseconds);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
playerController
|
playerController
|
||||||
..addStatusLister(playerListener)
|
..addStatusLister(playerListener)
|
||||||
@ -61,8 +58,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
if (p0 && !_plDanmakuController.initiated) {
|
if (p0 && !_plDanmakuController.initiated) {
|
||||||
_plDanmakuController.initiate(
|
_plDanmakuController.initiate(
|
||||||
playerController.duration.value.inMilliseconds,
|
playerController.duration.value.inMilliseconds,
|
||||||
playerController.position.value.inMilliseconds
|
playerController.position.value.inMilliseconds);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
blockTypes = playerController.blockTypes;
|
blockTypes = playerController.blockTypes;
|
||||||
@ -98,17 +94,18 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
_plDanmakuController.getCurrentDanmaku(currentPosition);
|
_plDanmakuController.getCurrentDanmaku(currentPosition);
|
||||||
|
|
||||||
if (currentDanmakuList != null) {
|
if (currentDanmakuList != null) {
|
||||||
Color? defaultColor = playerController.blockTypes.contains(6) ?
|
Color? defaultColor = playerController.blockTypes.contains(6)
|
||||||
DmUtils.decimalToColor(16777215) : null;
|
? DmUtils.decimalToColor(16777215)
|
||||||
|
: null;
|
||||||
|
|
||||||
_controller!.addItems(
|
_controller!.addItems(currentDanmakuList
|
||||||
currentDanmakuList.map((e) => DanmakuItem(
|
.map((e) => DanmakuItem(
|
||||||
e.content,
|
e.content,
|
||||||
color: defaultColor ?? DmUtils.decimalToColor(e.color),
|
color: defaultColor ?? DmUtils.decimalToColor(e.color),
|
||||||
time: e.progress,
|
time: e.progress,
|
||||||
type: DmUtils.getPosition(e.mode),
|
type: DmUtils.getPosition(e.mode),
|
||||||
)).toList()
|
))
|
||||||
);
|
.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +134,8 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
hideTop: blockTypes.contains(5),
|
hideTop: blockTypes.contains(5),
|
||||||
hideScroll: blockTypes.contains(2),
|
hideScroll: blockTypes.contains(2),
|
||||||
hideBottom: blockTypes.contains(4),
|
hideBottom: blockTypes.contains(4),
|
||||||
duration: danmakuDurationVal / widget.playerController.playbackSpeed,
|
duration:
|
||||||
|
danmakuDurationVal / widget.playerController.playbackSpeed,
|
||||||
// initDuration /
|
// initDuration /
|
||||||
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import 'package:pilipala/common/skeleton/video_reply.dart';
|
|||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/models/common/reply_type.dart';
|
import 'package:pilipala/models/common/reply_type.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
import 'package:pilipala/pages/dynamics/detail/index.dart';
|
||||||
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
|
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
import 'package:pilipala/pages/video/detail/reply_reply/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
|||||||
@ -139,7 +139,7 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
int liveLen = liveList.length;
|
int liveLen = liveList.length;
|
||||||
int upLen = upList.length;
|
int upLen = upList.length;
|
||||||
double itemWidth = contentWidth + itemPadding.horizontal;
|
double itemWidth = contentWidth + itemPadding.horizontal;
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
double screenWidth = MediaQuery.sizeOf(context).width;
|
||||||
double moveDistance = 0.0;
|
double moveDistance = 0.0;
|
||||||
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
||||||
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import 'package:pilipala/common/skeleton/video_card_h.dart';
|
|||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
import 'package:pilipala/pages/favDetail/index.dart';
|
import 'package:pilipala/pages/fav_detail/index.dart';
|
||||||
|
|
||||||
import 'widget/fav_video_card.dart';
|
import 'widget/fav_video_card.dart';
|
||||||
|
|
||||||
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
import 'package:pilipala/pages/favDetail/widget/fav_video_card.dart';
|
import 'package:pilipala/pages/fav_detail/widget/fav_video_card.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
|||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/models/common/reply_type.dart';
|
import 'package:pilipala/models/common/reply_type.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
import 'package:pilipala/pages/video/detail/reply_reply/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/video/play/url.dart';
|
import 'package:pilipala/models/video/play/url.dart';
|
||||||
import 'package:pilipala/pages/liveRoom/index.dart';
|
import 'package:pilipala/pages/live_room/index.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
@ -88,9 +88,9 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Box localCache = GStrorage.localCache;
|
Box localCache = GStrorage.localCache;
|
||||||
double statusBarHeight = MediaQuery.of(context).padding.top;
|
double statusBarHeight = MediaQuery.of(context).padding.top;
|
||||||
double sheetHeight = MediaQuery.of(context).size.height -
|
double sheetHeight = MediaQuery.sizeOf(context).height -
|
||||||
MediaQuery.of(context).padding.top -
|
MediaQuery.of(context).padding.top -
|
||||||
MediaQuery.of(context).size.width * 9 / 16;
|
MediaQuery.sizeOf(context).width * 9 / 16;
|
||||||
localCache.put('sheetHeight', sheetHeight);
|
localCache.put('sheetHeight', sheetHeight);
|
||||||
localCache.put('statusBarHeight', statusBarHeight);
|
localCache.put('statusBarHeight', statusBarHeight);
|
||||||
return PopScope(
|
return PopScope(
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
_memberCoinsFuture = _memberController.getRecentCoinVideo();
|
_memberCoinsFuture = _memberController.getRecentCoinVideo();
|
||||||
_extendNestCtr.addListener(
|
_extendNestCtr.addListener(
|
||||||
() {
|
() {
|
||||||
double offset = _extendNestCtr.position.pixels;
|
final double offset = _extendNestCtr.position.pixels;
|
||||||
if (offset > 100) {
|
if (offset > 100) {
|
||||||
appbarStream.add(true);
|
appbarStream.add(true);
|
||||||
} else {
|
} else {
|
||||||
@ -67,7 +67,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
title: StreamBuilder(
|
title: StreamBuilder(
|
||||||
stream: appbarStream.stream,
|
stream: appbarStream.stream,
|
||||||
initialData: false,
|
initialData: false,
|
||||||
builder: (context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
return AnimatedOpacity(
|
return AnimatedOpacity(
|
||||||
opacity: snapshot.data ? 1 : 0,
|
opacity: snapshot.data ? 1 : 0,
|
||||||
curve: Curves.easeOut,
|
curve: Curves.easeOut,
|
||||||
|
|||||||
@ -87,7 +87,7 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
|
|||||||
slivers: [
|
slivers: [
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data != null) {
|
if (snapshot.data != null) {
|
||||||
Map data = snapshot.data as Map;
|
Map data = snapshot.data as Map;
|
||||||
@ -97,7 +97,7 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
|
|||||||
() => list.isNotEmpty
|
() => list.isNotEmpty
|
||||||
? SliverList(
|
? SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(BuildContext context, index) {
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: list[index],
|
videoItem: list[index],
|
||||||
showOwner: false,
|
showOwner: false,
|
||||||
|
|||||||
@ -16,7 +16,7 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
|||||||
crossAxisSpacing: StyleString.cardSpace + 2,
|
crossAxisSpacing: StyleString.cardSpace + 2,
|
||||||
mainAxisSpacing: StyleString.cardSpace + 3,
|
mainAxisSpacing: StyleString.cardSpace + 3,
|
||||||
mainAxisExtent:
|
mainAxisExtent:
|
||||||
MediaQuery.of(context).size.width / 2 / StyleString.aspectRatio +
|
MediaQuery.sizeOf(context).width / 2 / StyleString.aspectRatio +
|
||||||
66 * MediaQuery.of(context).textScaleFactor),
|
66 * MediaQuery.of(context).textScaleFactor),
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@ -3,7 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
import 'package:pilipala/pages/searchPanel/index.dart';
|
import 'package:pilipala/pages/search_panel/index.dart';
|
||||||
|
|
||||||
class SearchVideoPanel extends StatelessWidget {
|
class SearchVideoPanel extends StatelessWidget {
|
||||||
SearchVideoPanel({
|
SearchVideoPanel({
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
import 'package:pilipala/pages/searchPanel/index.dart';
|
import 'package:pilipala/pages/search_panel/index.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
class SearchResultPage extends StatefulWidget {
|
class SearchResultPage extends StatefulWidget {
|
||||||
@ -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/quality.dart';
|
||||||
import 'package:pilipala/models/video/play/url.dart';
|
import 'package:pilipala/models/video/play/url.dart';
|
||||||
import 'package:pilipala/models/video/reply/item.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/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
@ -92,7 +92,7 @@ class VideoDetailController extends GetxController
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
Map argMap = Get.arguments;
|
final Map argMap = Get.arguments;
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
var keys = argMap.keys.toList();
|
var keys = argMap.keys.toList();
|
||||||
if (keys.isNotEmpty) {
|
if (keys.isNotEmpty) {
|
||||||
@ -188,8 +188,8 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
/// 根据currentAudioQa 重新设置audioUrl
|
/// 根据currentAudioQa 重新设置audioUrl
|
||||||
if (currentAudioQa != null) {
|
if (currentAudioQa != null) {
|
||||||
AudioItem firstAudio = data.dash!.audio!.firstWhere(
|
final AudioItem firstAudio = data.dash!.audio!.firstWhere(
|
||||||
(i) => i.id == currentAudioQa!.code,
|
(AudioItem i) => i.id == currentAudioQa!.code,
|
||||||
orElse: () => data.dash!.audio!.first,
|
orElse: () => data.dash!.audio!.first,
|
||||||
);
|
);
|
||||||
audioUrl = firstAudio.baseUrl ?? '';
|
audioUrl = firstAudio.baseUrl ?? '';
|
||||||
@ -246,7 +246,7 @@ class VideoDetailController extends GetxController
|
|||||||
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
data = result['data'];
|
data = result['data'];
|
||||||
List<VideoItem> allVideosList = data.dash!.video!;
|
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||||
try {
|
try {
|
||||||
// 当前可播放的最高质量视频
|
// 当前可播放的最高质量视频
|
||||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||||
@ -255,7 +255,7 @@ class VideoDetailController extends GetxController
|
|||||||
int resVideoQa = currentHighVideoQa;
|
int resVideoQa = currentHighVideoQa;
|
||||||
if (cacheVideoQa! <= currentHighVideoQa) {
|
if (cacheVideoQa! <= currentHighVideoQa) {
|
||||||
// 如果预设的画质低于当前最高
|
// 如果预设的画质低于当前最高
|
||||||
List<int> numbers = data.acceptQuality!
|
final List<int> numbers = data.acceptQuality!
|
||||||
.where((e) => e <= currentHighVideoQa)
|
.where((e) => e <= currentHighVideoQa)
|
||||||
.toList();
|
.toList();
|
||||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
||||||
@ -263,13 +263,13 @@ class VideoDetailController extends GetxController
|
|||||||
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
||||||
|
|
||||||
/// 取出符合当前画质的videoList
|
/// 取出符合当前画质的videoList
|
||||||
List<VideoItem> videosList =
|
final List<VideoItem> videosList =
|
||||||
allVideosList.where((e) => e.quality!.code == resVideoQa).toList();
|
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!;
|
supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!;
|
||||||
// 默认从设置中取AVC
|
// 默认从设置中取AVC
|
||||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
||||||
@ -304,7 +304,7 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
||||||
late AudioItem? firstAudio;
|
late AudioItem? firstAudio;
|
||||||
List<AudioItem> audiosList = data.dash!.audio!;
|
final List<AudioItem> audiosList = data.dash!.audio!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (data.dash!.dolby?.audio?.isNotEmpty == true) {
|
if (data.dash!.dolby?.audio?.isNotEmpty == true) {
|
||||||
@ -318,7 +318,7 @@ class VideoDetailController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (audiosList.isNotEmpty) {
|
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);
|
int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
|
||||||
if (!numbers.contains(cacheAudioQa) &&
|
if (!numbers.contains(cacheAudioQa) &&
|
||||||
numbers.any((e) => e > cacheAudioQa)) {
|
numbers.any((e) => e > cacheAudioQa)) {
|
||||||
|
|||||||
@ -389,7 +389,7 @@ class VideoIntroController extends GetxController {
|
|||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int currentStatus = followStatus['attribute'];
|
final int currentStatus = followStatus['attribute'];
|
||||||
int actionStatus = 0;
|
int actionStatus = 0;
|
||||||
switch (currentStatus) {
|
switch (currentStatus) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -467,7 +467,7 @@ class VideoIntroController extends GetxController {
|
|||||||
// 修改分P或番剧分集
|
// 修改分P或番剧分集
|
||||||
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
||||||
// 重新获取视频资源
|
// 重新获取视频资源
|
||||||
VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
videoDetailCtr.bvid = bvid;
|
videoDetailCtr.bvid = bvid;
|
||||||
videoDetailCtr.cid.value = cid;
|
videoDetailCtr.cid.value = cid;
|
||||||
@ -476,7 +476,7 @@ class VideoIntroController extends GetxController {
|
|||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
/// 未渲染回复组件时可能异常
|
/// 未渲染回复组件时可能异常
|
||||||
VideoReplyController videoReplyCtr =
|
final VideoReplyController videoReplyCtr =
|
||||||
Get.find<VideoReplyController>(tag: heroTag);
|
Get.find<VideoReplyController>(tag: heroTag);
|
||||||
videoReplyCtr.aid = aid;
|
videoReplyCtr.aid = aid;
|
||||||
videoReplyCtr.queryReplyList(type: 'init');
|
videoReplyCtr.queryReplyList(type: 'init');
|
||||||
@ -517,29 +517,27 @@ class VideoIntroController extends GetxController {
|
|||||||
|
|
||||||
/// 列表循环或者顺序播放时,自动播放下一个
|
/// 列表循环或者顺序播放时,自动播放下一个
|
||||||
void nextPlay() {
|
void nextPlay() {
|
||||||
late List episodes;
|
final List episodes = [];
|
||||||
bool isPages = false;
|
bool isPages = false;
|
||||||
if (videoDetail.value.ugcSeason != null) {
|
if (videoDetail.value.ugcSeason != null) {
|
||||||
UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||||
List<SectionItem> sections = ugcSeason.sections!;
|
final List<SectionItem> sections = ugcSeason.sections!;
|
||||||
episodes = [];
|
|
||||||
|
|
||||||
for (int i = 0; i < sections.length; i++) {
|
for (int i = 0; i < sections.length; i++) {
|
||||||
List<EpisodeItem> episodesList = sections[i].episodes!;
|
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||||
episodes.addAll(episodesList);
|
episodes.addAll(episodesList);
|
||||||
}
|
}
|
||||||
} else if (videoDetail.value.pages != null) {
|
} else if (videoDetail.value.pages != null) {
|
||||||
isPages = true;
|
isPages = true;
|
||||||
List<Part> pages = videoDetail.value.pages!;
|
final List<Part> pages = videoDetail.value.pages!;
|
||||||
episodes = [];
|
|
||||||
episodes.addAll(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;
|
int nextIndex = currentIndex + 1;
|
||||||
VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
|
|
||||||
// 列表循环
|
// 列表循环
|
||||||
if (nextIndex >= episodes.length) {
|
if (nextIndex >= episodes.length) {
|
||||||
@ -550,9 +548,9 @@ class VideoIntroController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int cid = episodes[nextIndex].cid!;
|
final int cid = episodes[nextIndex].cid!;
|
||||||
String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
||||||
int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
||||||
changeSeasonOrbangu(rBvid, cid, rAid);
|
changeSeasonOrbangu(rBvid, cid, rAid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,7 +565,7 @@ class VideoIntroController extends GetxController {
|
|||||||
// ai总结
|
// ai总结
|
||||||
Future aiConclusion() async {
|
Future aiConclusion() async {
|
||||||
SmartDialog.showLoading(msg: '正在生产ai总结');
|
SmartDialog.showLoading(msg: '正在生产ai总结');
|
||||||
var res = await VideoHttp.aiConclusion(
|
final res = await VideoHttp.aiConclusion(
|
||||||
bvid: bvid,
|
bvid: bvid,
|
||||||
cid: lastPlayCid.value,
|
cid: lastPlayCid.value,
|
||||||
upMid: videoDetail.value.owner!.mid!,
|
upMid: videoDetail.value.owner!.mid!,
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -26,7 +24,7 @@ import 'widgets/page.dart';
|
|||||||
import 'widgets/season.dart';
|
import 'widgets/season.dart';
|
||||||
|
|
||||||
class VideoIntroPanel extends StatefulWidget {
|
class VideoIntroPanel extends StatefulWidget {
|
||||||
const VideoIntroPanel({Key? key}) : super(key: key);
|
const VideoIntroPanel({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<VideoIntroPanel> createState() => _VideoIntroPanelState();
|
State<VideoIntroPanel> createState() => _VideoIntroPanelState();
|
||||||
@ -124,8 +122,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
late final VideoDetailController videoDetailCtr;
|
late final VideoDetailController videoDetailCtr;
|
||||||
late final Map<dynamic, dynamic> videoItem;
|
late final Map<dynamic, dynamic> videoItem;
|
||||||
|
|
||||||
Box localCache = GStrorage.localCache;
|
final Box<dynamic> localCache = GStrorage.localCache;
|
||||||
Box setting = GStrorage.setting;
|
final Box<dynamic> setting = GStrorage.setting;
|
||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
|
|
||||||
late final bool loadingStatus; // 加载状态
|
late final bool loadingStatus; // 加载状态
|
||||||
@ -138,12 +136,15 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
late bool enableAi;
|
late bool enableAi;
|
||||||
bool isProcessing = false;
|
bool isProcessing = false;
|
||||||
void Function()? handleState(Future Function() action) {
|
void Function()? handleState(Future Function() action) {
|
||||||
return isProcessing ? null : () async {
|
return isProcessing
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
setState(() => isProcessing = true);
|
setState(() => isProcessing = true);
|
||||||
await action();
|
await action();
|
||||||
setState(() => isProcessing = false);
|
setState(() => isProcessing = false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -168,7 +169,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool enableDragQuickFav =
|
final bool enableDragQuickFav =
|
||||||
setting.get(SettingBoxKey.enableQuickFav, defaultValue: false);
|
setting.get(SettingBoxKey.enableQuickFav, defaultValue: false);
|
||||||
// 快速收藏 &
|
// 快速收藏 &
|
||||||
// 点按 收藏至默认文件夹
|
// 点按 收藏至默认文件夹
|
||||||
@ -182,7 +183,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return FavPanel(ctr: videoIntroController);
|
return FavPanel(ctr: videoIntroController);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -192,7 +193,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return FavPanel(ctr: videoIntroController);
|
return FavPanel(ctr: videoIntroController);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -202,7 +203,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return FavPanel(ctr: videoIntroController);
|
return FavPanel(ctr: videoIntroController);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -251,8 +252,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ThemeData t = Theme.of(context);
|
final ThemeData t = Theme.of(context);
|
||||||
Color outline = t.colorScheme.outline;
|
final Color outline = t.colorScheme.outline;
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10),
|
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10),
|
||||||
@ -333,7 +334,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
top: 6,
|
top: 6,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
var res =
|
final res =
|
||||||
await videoIntroController.aiConclusion();
|
await videoIntroController.aiConclusion();
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
showAiBottomSheet();
|
showAiBottomSheet();
|
||||||
@ -472,13 +473,14 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget actionGrid(BuildContext context, videoIntroController) {
|
Widget actionGrid(BuildContext context, videoIntroController) {
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(top: 6, bottom: 4),
|
margin: const EdgeInsets.only(top: 6, bottom: 4),
|
||||||
height: constraints.maxWidth / 5 * 0.8,
|
height: constraints.maxWidth / 5 * 0.8,
|
||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
primary: false,
|
primary: false,
|
||||||
padding: const EdgeInsets.all(0),
|
padding: EdgeInsets.zero,
|
||||||
crossAxisCount: 5,
|
crossAxisCount: 5,
|
||||||
childAspectRatio: 1.25,
|
childAspectRatio: 1.25,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -543,7 +545,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
|
Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
|
||||||
return Row(children: [
|
return Row(children: <Widget>[
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionRowItem(
|
() => ActionRowItem(
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||||
|
|||||||
@ -6,15 +6,15 @@ import 'package:pilipala/utils/feed_back.dart';
|
|||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class FavPanel extends StatefulWidget {
|
class FavPanel extends StatefulWidget {
|
||||||
final dynamic ctr;
|
|
||||||
const FavPanel({super.key, this.ctr});
|
const FavPanel({super.key, this.ctr});
|
||||||
|
final dynamic ctr;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FavPanel> createState() => _FavPanelState();
|
State<FavPanel> createState() => _FavPanelState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FavPanelState extends State<FavPanel> {
|
class _FavPanelState extends State<FavPanel> {
|
||||||
Box localCache = GStrorage.localCache;
|
final Box<dynamic> localCache = GStrorage.localCache;
|
||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
late Future _futureBuilderFuture;
|
late Future _futureBuilderFuture;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class _FavPanelState extends State<FavPanel> {
|
|||||||
child: Material(
|
child: Material(
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data as Map;
|
Map data = snapshot.data as Map;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
@ -109,7 +109,7 @@ class _FavPanelState extends State<FavPanel> {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class GroupPanel extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _GroupPanelState extends State<GroupPanel> {
|
class _GroupPanelState extends State<GroupPanel> {
|
||||||
Box localCache = GStrorage.localCache;
|
final Box<dynamic> localCache = GStrorage.localCache;
|
||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
late Future _futureBuilderFuture;
|
late Future _futureBuilderFuture;
|
||||||
late List<MemberTagItemModel> tagsList;
|
late List<MemberTagItemModel> tagsList;
|
||||||
@ -33,17 +33,20 @@ class _GroupPanelState extends State<GroupPanel> {
|
|||||||
void onSave() async {
|
void onSave() async {
|
||||||
feedBack();
|
feedBack();
|
||||||
// 是否有选中的 有选中的带id,没选使用默认0
|
// 是否有选中的 有选中的带id,没选使用默认0
|
||||||
bool anyHasChecked = tagsList.any((e) => e.checked == true);
|
final bool anyHasChecked =
|
||||||
|
tagsList.any((MemberTagItemModel e) => e.checked == true);
|
||||||
late String tagids;
|
late String tagids;
|
||||||
if (anyHasChecked) {
|
if (anyHasChecked) {
|
||||||
List checkedList = tagsList.where((e) => e.checked == true).toList();
|
final List<MemberTagItemModel> checkedList =
|
||||||
List<int> tagidList = checkedList.map<int>((e) => e.tagid).toList();
|
tagsList.where((MemberTagItemModel e) => e.checked == true).toList();
|
||||||
|
final List<int> tagidList =
|
||||||
|
checkedList.map<int>((e) => e.tagid!).toList();
|
||||||
tagids = tagidList.join(',');
|
tagids = tagidList.join(',');
|
||||||
} else {
|
} else {
|
||||||
tagids = '0';
|
tagids = '0';
|
||||||
}
|
}
|
||||||
// 保存
|
// 保存
|
||||||
var res = await MemberHttp.addUsers(widget.mid, tagids);
|
final res = await MemberHttp.addUsers(widget.mid, tagids);
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
Get.back();
|
Get.back();
|
||||||
@ -56,7 +59,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
|||||||
height: sheetHeight,
|
height: sheetHeight,
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
AppBar(
|
AppBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
@ -70,7 +73,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
|||||||
child: Material(
|
child: Material(
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data as Map;
|
Map data = snapshot.data as Map;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
|
|||||||
@ -12,12 +12,11 @@ Box localCache = GStrorage.localCache;
|
|||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
|
|
||||||
class IntroDetail extends StatelessWidget {
|
class IntroDetail extends StatelessWidget {
|
||||||
final dynamic videoDetail;
|
|
||||||
|
|
||||||
const IntroDetail({
|
const IntroDetail({
|
||||||
Key? key,
|
super.key,
|
||||||
this.videoDetail,
|
this.videoDetail,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
final dynamic videoDetail;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -86,13 +85,11 @@ class IntroDetail extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: SelectableRegion(
|
child: SelectableRegion(
|
||||||
magnifierConfiguration:
|
|
||||||
const TextMagnifierConfiguration(),
|
|
||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
selectionControls: MaterialTextSelectionControls(),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
videoDetail!.bvid!,
|
videoDetail!.bvid!,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
@ -122,20 +119,21 @@ class IntroDetail extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
InlineSpan buildContent(BuildContext context, content) {
|
InlineSpan buildContent(BuildContext context, content) {
|
||||||
List descV2 = content.descV2;
|
final List descV2 = content.descV2;
|
||||||
// type
|
// type
|
||||||
// 1 普通文本
|
// 1 普通文本
|
||||||
// 2 @用户
|
// 2 @用户
|
||||||
List<TextSpan> spanChilds = List.generate(descV2.length, (index) {
|
final List<TextSpan> spanChilds = List.generate(descV2.length, (index) {
|
||||||
final currentDesc = descV2[index];
|
final currentDesc = descV2[index];
|
||||||
switch (currentDesc.type) {
|
switch (currentDesc.type) {
|
||||||
case 1:
|
case 1:
|
||||||
List<InlineSpan> spanChildren = [];
|
final List<InlineSpan> spanChildren = <InlineSpan>[];
|
||||||
RegExp urlRegExp = RegExp(r'https?://\S+\b');
|
final RegExp urlRegExp = RegExp(r'https?://\S+\b');
|
||||||
Iterable<Match> matches = urlRegExp.allMatches(currentDesc.rawText);
|
final Iterable<Match> matches =
|
||||||
|
urlRegExp.allMatches(currentDesc.rawText);
|
||||||
|
|
||||||
int previousEndIndex = 0;
|
int previousEndIndex = 0;
|
||||||
for (Match match in matches) {
|
for (final Match match in matches) {
|
||||||
if (match.start > previousEndIndex) {
|
if (match.start > previousEndIndex) {
|
||||||
spanChildren.add(TextSpan(
|
spanChildren.add(TextSpan(
|
||||||
text: currentDesc.rawText
|
text: currentDesc.rawText
|
||||||
@ -172,11 +170,12 @@ class IntroDetail extends StatelessWidget {
|
|||||||
text: currentDesc.rawText.substring(previousEndIndex)));
|
text: currentDesc.rawText.substring(previousEndIndex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSpan result = TextSpan(children: spanChildren);
|
final TextSpan result = TextSpan(children: spanChildren);
|
||||||
return result;
|
return result;
|
||||||
case 2:
|
case 2:
|
||||||
final colorSchemePrimary = Theme.of(context).colorScheme.primary;
|
final Color colorSchemePrimary =
|
||||||
final heroTag = Utils.makeHeroTag(currentDesc.bizId);
|
Theme.of(context).colorScheme.primary;
|
||||||
|
final String heroTag = Utils.makeHeroTag(currentDesc.bizId);
|
||||||
return TextSpan(
|
return TextSpan(
|
||||||
text: '@${currentDesc.rawText}',
|
text: '@${currentDesc.rawText}',
|
||||||
style: TextStyle(color: colorSchemePrimary),
|
style: TextStyle(color: colorSchemePrimary),
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
|
||||||
class MenuRow extends StatelessWidget {
|
class MenuRow extends StatelessWidget {
|
||||||
final bool? loadingStatus;
|
|
||||||
const MenuRow({
|
const MenuRow({
|
||||||
Key? key,
|
super.key,
|
||||||
this.loadingStatus,
|
this.loadingStatus,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
final bool? loadingStatus;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -50,7 +50,7 @@ class MenuRow extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget actionRowLineItem(
|
Widget actionRowLineItem(
|
||||||
context, Function? onTap, bool? loadingStatus, String? text,
|
BuildContext context, Function? onTap, bool? loadingStatus, String? text,
|
||||||
{bool selectStatus = false}) {
|
{bool selectStatus = false}) {
|
||||||
return Material(
|
return Material(
|
||||||
color: selectStatus
|
color: selectStatus
|
||||||
@ -97,18 +97,18 @@ class MenuRow extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ActionRowLineItem extends StatelessWidget {
|
class ActionRowLineItem extends StatelessWidget {
|
||||||
|
const ActionRowLineItem({
|
||||||
|
super.key,
|
||||||
|
this.selectStatus,
|
||||||
|
this.onTap,
|
||||||
|
this.text,
|
||||||
|
this.loadingStatus = false,
|
||||||
|
});
|
||||||
final bool? selectStatus;
|
final bool? selectStatus;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
final bool? loadingStatus;
|
final bool? loadingStatus;
|
||||||
final String? text;
|
final String? text;
|
||||||
|
|
||||||
const ActionRowLineItem(
|
|
||||||
{super.key,
|
|
||||||
this.selectStatus,
|
|
||||||
this.onTap,
|
|
||||||
this.text,
|
|
||||||
this.loadingStatus = false});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
|
|||||||
@ -4,11 +4,6 @@ import 'package:pilipala/models/video_detail_res.dart';
|
|||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
|
|
||||||
class PagesPanel extends StatefulWidget {
|
class PagesPanel extends StatefulWidget {
|
||||||
final List<Part> pages;
|
|
||||||
final int? cid;
|
|
||||||
final double? sheetHeight;
|
|
||||||
final Function? changeFuc;
|
|
||||||
|
|
||||||
const PagesPanel({
|
const PagesPanel({
|
||||||
super.key,
|
super.key,
|
||||||
required this.pages,
|
required this.pages,
|
||||||
@ -16,6 +11,10 @@ class PagesPanel extends StatefulWidget {
|
|||||||
this.sheetHeight,
|
this.sheetHeight,
|
||||||
this.changeFuc,
|
this.changeFuc,
|
||||||
});
|
});
|
||||||
|
final List<Part> pages;
|
||||||
|
final int? cid;
|
||||||
|
final double? sheetHeight;
|
||||||
|
final Function? changeFuc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PagesPanel> createState() => _PagesPanelState();
|
State<PagesPanel> createState() => _PagesPanelState();
|
||||||
@ -25,7 +24,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
late List<Part> episodes;
|
late List<Part> episodes;
|
||||||
late int cid;
|
late int cid;
|
||||||
late int currentIndex;
|
late int currentIndex;
|
||||||
String heroTag = Get.arguments['heroTag'];
|
final String heroTag = Get.arguments['heroTag'];
|
||||||
late VideoDetailController _videoDetailController;
|
late VideoDetailController _videoDetailController;
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@ -35,11 +34,11 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
cid = widget.cid!;
|
cid = widget.cid!;
|
||||||
episodes = widget.pages;
|
episodes = widget.pages;
|
||||||
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
|
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
currentIndex = episodes.indexWhere((Part e) => e.cid == cid);
|
||||||
_videoDetailController.cid.listen((p0) {
|
_videoDetailController.cid.listen((int p0) {
|
||||||
cid = p0;
|
cid = p0;
|
||||||
setState(() {});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 10, bottom: 2),
|
padding: const EdgeInsets.only(top: 10, bottom: 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -133,7 +132,8 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
itemCount: episodes.length,
|
itemCount: episodes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder:
|
||||||
|
(BuildContext context, int index) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
changeFucCall(
|
changeFucCall(
|
||||||
@ -191,7 +191,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: widget.pages.length,
|
itemCount: widget.pages.length,
|
||||||
itemExtent: 150,
|
itemExtent: 150,
|
||||||
itemBuilder: ((context, i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 150,
|
width: 150,
|
||||||
margin: const EdgeInsets.only(right: 10),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
@ -205,8 +205,8 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 8, horizontal: 8),
|
vertical: 8, horizontal: 8),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
if (i == currentIndex) ...[
|
if (i == currentIndex) ...<Widget>[
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/live.gif',
|
'assets/images/live.gif',
|
||||||
color: Theme.of(context).colorScheme.primary,
|
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';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
|
||||||
class SeasonPanel extends StatefulWidget {
|
class SeasonPanel extends StatefulWidget {
|
||||||
final UgcSeason ugcSeason;
|
|
||||||
final int? cid;
|
|
||||||
final double? sheetHeight;
|
|
||||||
final Function? changeFuc;
|
|
||||||
|
|
||||||
const SeasonPanel({
|
const SeasonPanel({
|
||||||
super.key,
|
super.key,
|
||||||
required this.ugcSeason,
|
required this.ugcSeason,
|
||||||
@ -18,6 +13,10 @@ class SeasonPanel extends StatefulWidget {
|
|||||||
this.sheetHeight,
|
this.sheetHeight,
|
||||||
this.changeFuc,
|
this.changeFuc,
|
||||||
});
|
});
|
||||||
|
final UgcSeason ugcSeason;
|
||||||
|
final int? cid;
|
||||||
|
final double? sheetHeight;
|
||||||
|
final Function? changeFuc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SeasonPanel> createState() => _SeasonPanelState();
|
State<SeasonPanel> createState() => _SeasonPanelState();
|
||||||
@ -27,7 +26,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
late List<EpisodeItem> episodes;
|
late List<EpisodeItem> episodes;
|
||||||
late int cid;
|
late int cid;
|
||||||
late int currentIndex;
|
late int currentIndex;
|
||||||
String heroTag = Get.arguments['heroTag'];
|
final String heroTag = Get.arguments['heroTag'];
|
||||||
late VideoDetailController _videoDetailController;
|
late VideoDetailController _videoDetailController;
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
final ItemScrollController itemScrollController = ItemScrollController();
|
final ItemScrollController itemScrollController = ItemScrollController();
|
||||||
@ -41,9 +40,9 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
/// 根据 cid 找到对应集,找到对应 episodes
|
/// 根据 cid 找到对应集,找到对应 episodes
|
||||||
/// 有多个episodes时,只显示其中一个
|
/// 有多个episodes时,只显示其中一个
|
||||||
/// TODO 同时显示多个合集
|
/// TODO 同时显示多个合集
|
||||||
List<SectionItem> sections = widget.ugcSeason.sections!;
|
final List<SectionItem> sections = widget.ugcSeason.sections!;
|
||||||
for (int i = 0; i < sections.length; i++) {
|
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++) {
|
for (int j = 0; j < episodesList.length; j++) {
|
||||||
if (episodesList[j].cid == cid) {
|
if (episodesList[j].cid == cid) {
|
||||||
episodes = episodesList;
|
episodes = episodesList;
|
||||||
@ -56,22 +55,21 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
// episodes = widget.ugcSeason.sections!
|
// episodes = widget.ugcSeason.sections!
|
||||||
// .firstWhere((e) => e.seasonId == widget.ugcSeason.id)
|
// .firstWhere((e) => e.seasonId == widget.ugcSeason.id)
|
||||||
// .episodes!;
|
// .episodes!;
|
||||||
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
currentIndex = episodes.indexWhere((EpisodeItem e) => e.cid == cid);
|
||||||
_videoDetailController.cid.listen((p0) {
|
_videoDetailController.cid.listen((int p0) {
|
||||||
cid = p0;
|
cid = p0;
|
||||||
setState(() {});
|
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!(
|
await widget.changeFuc!(
|
||||||
IdUtils.av2bv(item.aid),
|
IdUtils.av2bv(item.aid),
|
||||||
item.cid,
|
item.cid,
|
||||||
item.aid,
|
item.aid,
|
||||||
);
|
);
|
||||||
currentIndex = i;
|
currentIndex = i;
|
||||||
setState(() {});
|
|
||||||
Get.back();
|
Get.back();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
@ -84,7 +82,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Builder(builder: (context) {
|
return Builder(builder: (BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
top: 8,
|
top: 8,
|
||||||
@ -136,7 +134,8 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
child: Material(
|
child: Material(
|
||||||
child: ScrollablePositionedList.builder(
|
child: ScrollablePositionedList.builder(
|
||||||
itemCount: episodes.length,
|
itemCount: episodes.length,
|
||||||
itemBuilder: (context, index) => ListTile(
|
itemBuilder: (BuildContext context, int index) =>
|
||||||
|
ListTile(
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
changeFucCall(episodes[index], index),
|
changeFucCall(episodes[index], index),
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -174,7 +173,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'合集:${widget.ugcSeason.title!}',
|
'合集:${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 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
import './controller.dart';
|
import './controller.dart';
|
||||||
|
|
||||||
class RelatedVideoPanel extends StatefulWidget {
|
class RelatedVideoPanel extends StatelessWidget {
|
||||||
const RelatedVideoPanel({super.key});
|
|
||||||
@override
|
|
||||||
State<RelatedVideoPanel> createState() => _RelatedVideoPanelState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
|
|
||||||
final ReleatedController _releatedController =
|
final ReleatedController _releatedController =
|
||||||
Get.put(ReleatedController(), tag: Get.arguments['heroTag']);
|
Get.put(ReleatedController(), tag: Get.arguments['heroTag']);
|
||||||
|
|
||||||
@ -21,7 +15,7 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: _releatedController.queryRelatedVideo(),
|
future: _releatedController.queryRelatedVideo(),
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
@ -72,7 +66,7 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel> {
|
|||||||
|
|
||||||
OverlayEntry _createPopupDialog(videoItem) {
|
OverlayEntry _createPopupDialog(videoItem) {
|
||||||
return OverlayEntry(
|
return OverlayEntry(
|
||||||
builder: (context) => AnimatedDialog(
|
builder: (BuildContext context) => AnimatedDialog(
|
||||||
closeFn: _releatedController.popupDialog?.remove,
|
closeFn: _releatedController.popupDialog?.remove,
|
||||||
child: OverlayPop(
|
child: OverlayPop(
|
||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
|
|||||||
@ -41,8 +41,8 @@ class VideoReplyController extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
int deaultReplySortIndex =
|
final int deaultReplySortIndex =
|
||||||
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
|
setting.get(SettingBoxKey.replySortType, defaultValue: 0) as int;
|
||||||
_sortType = ReplySortType.values[deaultReplySortIndex];
|
_sortType = ReplySortType.values[deaultReplySortIndex];
|
||||||
sortTypeTitle.value = _sortType.titles;
|
sortTypeTitle.value = _sortType.titles;
|
||||||
sortTypeLabel.value = _sortType.labels;
|
sortTypeLabel.value = _sortType.labels;
|
||||||
@ -56,7 +56,7 @@ class VideoReplyController extends GetxController {
|
|||||||
if (noMore.value == '没有更多了') {
|
if (noMore.value == '没有更多了') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var res = await ReplyHttp.replyList(
|
final res = await ReplyHttp.replyList(
|
||||||
oid: aid!,
|
oid: aid!,
|
||||||
pageNum: currentPage + 1,
|
pageNum: currentPage + 1,
|
||||||
ps: ps,
|
ps: ps,
|
||||||
@ -64,7 +64,7 @@ class VideoReplyController extends GetxController {
|
|||||||
sort: _sortType.index,
|
sort: _sortType.index,
|
||||||
);
|
);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
List<ReplyItemModel> replies = res['data'].replies;
|
final List<ReplyItemModel> replies = res['data'].replies;
|
||||||
if (replies.isNotEmpty) {
|
if (replies.isNotEmpty) {
|
||||||
noMore.value = '加载中...';
|
noMore.value = '加载中...';
|
||||||
|
|
||||||
@ -84,9 +84,8 @@ class VideoReplyController extends GetxController {
|
|||||||
if (type == 'init') {
|
if (type == 'init') {
|
||||||
// 添加置顶回复
|
// 添加置顶回复
|
||||||
if (res['data'].upper.top != null) {
|
if (res['data'].upper.top != null) {
|
||||||
bool flag = res['data']
|
final bool flag = res['data'].topReplies.any((ReplyItemModel reply) =>
|
||||||
.topReplies
|
reply.rpid == res['data'].upper.top.rpid) as bool;
|
||||||
.any((reply) => reply.rpid == res['data'].upper.top.rpid);
|
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
replies.insert(0, res['data'].upper.top);
|
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/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/models/common/reply_type.dart';
|
import 'package:pilipala/models/common/reply_type.dart';
|
||||||
import 'package:pilipala/pages/video/detail/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/feed_back.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@ -107,7 +107,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
|
|
||||||
// 展示二级回复
|
// 展示二级回复
|
||||||
void replyReply(replyItem) {
|
void replyReply(replyItem) {
|
||||||
VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
if (replyItem != null) {
|
if (replyItem != null) {
|
||||||
videoDetailCtr.oid = replyItem.oid;
|
videoDetailCtr.oid = replyItem.oid;
|
||||||
@ -193,7 +193,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
var data = snapshot.data;
|
var data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
@ -203,13 +203,13 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
_videoReplyController.replyList.isEmpty
|
_videoReplyController.replyList.isEmpty
|
||||||
? SliverList(
|
? SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(BuildContext context, index) {
|
||||||
return const VideoReplySkeleton();
|
return const VideoReplySkeleton();
|
||||||
}, childCount: 5),
|
}, childCount: 5),
|
||||||
)
|
)
|
||||||
: SliverList(
|
: SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(BuildContext context, index) {
|
||||||
double bottom =
|
double bottom =
|
||||||
MediaQuery.of(context).padding.bottom;
|
MediaQuery.of(context).padding.bottom;
|
||||||
if (index ==
|
if (index ==
|
||||||
@ -262,7 +262,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverList(
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, index) {
|
||||||
return const VideoReplySkeleton();
|
return const VideoReplySkeleton();
|
||||||
}, childCount: 5),
|
}, childCount: 5),
|
||||||
);
|
);
|
||||||
@ -318,12 +319,11 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||||
|
_MySliverPersistentHeaderDelegate({required this.child});
|
||||||
final double _minExtent = 45;
|
final double _minExtent = 45;
|
||||||
final double _maxExtent = 45;
|
final double _maxExtent = 45;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
_MySliverPersistentHeaderDelegate({required this.child});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(
|
Widget build(
|
||||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
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/models/video/reply/item.dart';
|
||||||
import 'package:pilipala/pages/preview/index.dart';
|
import 'package:pilipala/pages/preview/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/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/feed_back.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
@ -27,8 +27,8 @@ class ReplyItem extends StatelessWidget {
|
|||||||
this.showReplyRow = true,
|
this.showReplyRow = true,
|
||||||
this.replyReply,
|
this.replyReply,
|
||||||
this.replyType,
|
this.replyType,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
final ReplyItemModel? replyItem;
|
final ReplyItemModel? replyItem;
|
||||||
final Function? addReply;
|
final Function? addReply;
|
||||||
final String? replyLevel;
|
final String? replyLevel;
|
||||||
@ -68,7 +68,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget lfAvtar(context, heroTag) {
|
Widget lfAvtar(BuildContext context, String heroTag) {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
@ -117,8 +117,8 @@ class ReplyItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget content(context) {
|
Widget content(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(replyItem!.mid);
|
final String heroTag = Utils.makeHeroTag(replyItem!.mid);
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -260,7 +260,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
Utils.dateFormat(replyItem!.ctime),
|
Utils.dateFormat(replyItem!.ctime),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -291,7 +291,6 @@ class ReplyItem extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
||||||
child: SelectableRegion(
|
child: SelectableRegion(
|
||||||
magnifierConfiguration: const TextMagnifierConfiguration(),
|
|
||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
selectionControls: MaterialTextSelectionControls(),
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
@ -340,9 +339,9 @@ class ReplyItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 感谢、回复、复制
|
// 感谢、回复、复制
|
||||||
Widget bottonAction(context, replyControl) {
|
Widget bottonAction(BuildContext context, replyControl) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
const SizedBox(width: 32),
|
const SizedBox(width: 32),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 32,
|
height: 32,
|
||||||
@ -422,7 +421,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
this.replyItem,
|
this.replyItem,
|
||||||
this.replyReply,
|
this.replyReply,
|
||||||
});
|
});
|
||||||
List? replies;
|
final List? replies;
|
||||||
ReplyControl? replyControl;
|
ReplyControl? replyControl;
|
||||||
// int? f_rpid;
|
// int? f_rpid;
|
||||||
ReplyItemModel? replyItem;
|
ReplyItemModel? replyItem;
|
||||||
@ -430,8 +429,8 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isShow = replyControl!.isShow!;
|
final bool isShow = replyControl!.isShow!;
|
||||||
int extraRow = replyControl != null && isShow ? 1 : 0;
|
final int extraRow = replyControl != null && isShow ? 1 : 0;
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
|
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
|
||||||
child: Material(
|
child: Material(
|
||||||
@ -443,7 +442,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (replies!.isNotEmpty)
|
if (replies!.isNotEmpty)
|
||||||
for (var i = 0; i < replies!.length; i++) ...[
|
for (int i = 0; i < replies!.length; i++) ...[
|
||||||
InkWell(
|
InkWell(
|
||||||
// 一楼点击评论展开评论详情
|
// 一楼点击评论展开评论详情
|
||||||
onTap: () => replyReply!(replyItem),
|
onTap: () => replyReply!(replyItem),
|
||||||
@ -472,7 +471,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
feedBack();
|
feedBack();
|
||||||
String heroTag =
|
final String heroTag =
|
||||||
Utils.makeHeroTag(replies![i].member.mid);
|
Utils.makeHeroTag(replies![i].member.mid);
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/member?mid=${replies![i].member.mid}',
|
'/member?mid=${replies![i].member.mid}',
|
||||||
@ -539,7 +538,7 @@ InlineSpan buildContent(
|
|||||||
// replyItem 当前回复内容
|
// replyItem 当前回复内容
|
||||||
// replyReply 查看二楼回复(回复详情)回调
|
// replyReply 查看二楼回复(回复详情)回调
|
||||||
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
||||||
var content = replyItem.content;
|
final content = replyItem.content;
|
||||||
if (content.emote.isEmpty &&
|
if (content.emote.isEmpty &&
|
||||||
content.atNameToMid.isEmpty &&
|
content.atNameToMid.isEmpty &&
|
||||||
content.jumpUrl.isEmpty &&
|
content.jumpUrl.isEmpty &&
|
||||||
@ -552,7 +551,7 @@ InlineSpan buildContent(
|
|||||||
() => replyReply(replyItem.root == 0 ? replyItem : fReplyItem),
|
() => replyReply(replyItem.root == 0 ? replyItem : fReplyItem),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
List<InlineSpan> spanChilds = [];
|
final List<InlineSpan> spanChilds = <InlineSpan>[];
|
||||||
bool hasMatchMember = true;
|
bool hasMatchMember = true;
|
||||||
|
|
||||||
// 投票
|
// 投票
|
||||||
@ -590,11 +589,11 @@ InlineSpan buildContent(
|
|||||||
content.message.splitMapJoin(
|
content.message.splitMapJoin(
|
||||||
RegExp(r"\[.*?\]"),
|
RegExp(r"\[.*?\]"),
|
||||||
onMatch: (Match match) {
|
onMatch: (Match match) {
|
||||||
String matchStr = match[0]!;
|
final String matchStr = match[0]!;
|
||||||
if (content.emote.isNotEmpty &&
|
if (content.emote.isNotEmpty &&
|
||||||
matchStr.indexOf('[') == matchStr.lastIndexOf('[') &&
|
matchStr.indexOf('[') == matchStr.lastIndexOf('[') &&
|
||||||
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)) {
|
if (content.emote.keys.contains(matchStr)) {
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
@ -628,7 +627,7 @@ InlineSpan buildContent(
|
|||||||
// 匹配@用户
|
// 匹配@用户
|
||||||
String matchMember = str;
|
String matchMember = str;
|
||||||
if (content.atNameToMid.isNotEmpty) {
|
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('|'));
|
RegExp reg = RegExp(atNameToMidKeys.map((key) => key).join('|'));
|
||||||
// if (!content.message.contains(':')) {
|
// if (!content.message.contains(':')) {
|
||||||
// reg = RegExp(r"@.*( |:)");
|
// reg = RegExp(r"@.*( |:)");
|
||||||
@ -667,7 +666,7 @@ InlineSpan buildContent(
|
|||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
String heroTag = Utils.makeHeroTag(value);
|
final String heroTag = Utils.makeHeroTag(value);
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/member?mid=$value',
|
'/member?mid=$value',
|
||||||
arguments: {'face': '', 'heroTag': heroTag},
|
arguments: {'face': '', 'heroTag': heroTag},
|
||||||
@ -693,8 +692,8 @@ InlineSpan buildContent(
|
|||||||
// 匹配 jumpUrl
|
// 匹配 jumpUrl
|
||||||
String matchUrl = matchMember;
|
String matchUrl = matchMember;
|
||||||
if (content.jumpUrl.isNotEmpty && hasMatchMember) {
|
if (content.jumpUrl.isNotEmpty && hasMatchMember) {
|
||||||
List urlKeys = content.jumpUrl.keys.toList().reversed.toList();
|
final List urlKeys = content.jumpUrl.keys.toList().reversed.toList();
|
||||||
for (var index = 0; index < urlKeys.length; index++) {
|
for (int index = 0; index < urlKeys.length; index++) {
|
||||||
var i = urlKeys[index];
|
var i = urlKeys[index];
|
||||||
if (i.contains('?')) {
|
if (i.contains('?')) {
|
||||||
urlKeys[index] = i.replaceAll('?', '\\?');
|
urlKeys[index] = i.replaceAll('?', '\\?');
|
||||||
@ -711,14 +710,14 @@ InlineSpan buildContent(
|
|||||||
RegExp(urlKeys.map((key) => key).join("|")),
|
RegExp(urlKeys.map((key) => key).join("|")),
|
||||||
// RegExp('What does the fox say\\?'),
|
// RegExp('What does the fox say\\?'),
|
||||||
onMatch: (Match match) {
|
onMatch: (Match match) {
|
||||||
String matchStr = match[0]!;
|
final String matchStr = match[0]!;
|
||||||
String appUrlSchema = '';
|
String appUrlSchema = '';
|
||||||
if (content.jumpUrl[matchStr] != null) {
|
if (content.jumpUrl[matchStr] != null) {
|
||||||
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
|
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
|
||||||
}
|
}
|
||||||
// 默认不显示关键词
|
// 默认不显示关键词
|
||||||
bool enableWordRe =
|
final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
|
||||||
setting.get(SettingBoxKey.enableWordRe, defaultValue: false);
|
defaultValue: false) as bool;
|
||||||
if (content.jumpUrl[matchStr] != null) {
|
if (content.jumpUrl[matchStr] != null) {
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
@ -731,9 +730,9 @@ InlineSpan buildContent(
|
|||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
if (appUrlSchema == '') {
|
if (appUrlSchema == '') {
|
||||||
String str = Uri.parse(matchStr).pathSegments[0];
|
final String str = Uri.parse(matchStr).pathSegments[0];
|
||||||
Map matchRes = IdUtils.matchAvorBv(input: str);
|
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||||
List matchKeys = matchRes.keys.toList();
|
final List matchKeys = matchRes.keys.toList();
|
||||||
if (matchKeys.isNotEmpty) {
|
if (matchKeys.isNotEmpty) {
|
||||||
if (matchKeys.first == 'BV') {
|
if (matchKeys.first == 'BV') {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
@ -834,8 +833,8 @@ InlineSpan buildContent(
|
|||||||
|
|
||||||
// 图片渲染
|
// 图片渲染
|
||||||
if (content.pictures.isNotEmpty) {
|
if (content.pictures.isNotEmpty) {
|
||||||
List<String> picList = [];
|
final List<String> picList = <String>[];
|
||||||
int len = content.pictures.length;
|
final int len = content.pictures.length;
|
||||||
if (len == 1) {
|
if (len == 1) {
|
||||||
Map pictureItem = content.pictures.first;
|
Map pictureItem = content.pictures.first;
|
||||||
picList.add(pictureItem['img_src']);
|
picList.add(pictureItem['img_src']);
|
||||||
@ -843,13 +842,13 @@ InlineSpan buildContent(
|
|||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (BuildContext context, BoxConstraints box) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
useSafeArea: false,
|
useSafeArea: false,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return ImagePreview(initialPage: 0, imgList: picList);
|
return ImagePreview(initialPage: 0, imgList: picList);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -25,12 +25,12 @@ class _ZanButtonState extends State<ZanButton> {
|
|||||||
Future onLikeReply() async {
|
Future onLikeReply() async {
|
||||||
feedBack();
|
feedBack();
|
||||||
// SmartDialog.showLoading(msg: 'pilipala ...');
|
// SmartDialog.showLoading(msg: 'pilipala ...');
|
||||||
ReplyItemModel replyItem = widget.replyItem!;
|
final ReplyItemModel replyItem = widget.replyItem!;
|
||||||
int oid = replyItem.oid!;
|
final int oid = replyItem.oid!;
|
||||||
int rpid = replyItem.rpid!;
|
final int rpid = replyItem.rpid!;
|
||||||
// 1 已点赞 2 不喜欢 0 未操作
|
// 1 已点赞 2 不喜欢 0 未操作
|
||||||
int action = replyItem.action == 0 ? 1 : 0;
|
final int action = replyItem.action == 0 ? 1 : 0;
|
||||||
var res = await ReplyHttp.likeReply(
|
final res = await ReplyHttp.likeReply(
|
||||||
type: widget.replyType!.index, oid: oid, rpid: rpid, action: action);
|
type: widget.replyType!.index, oid: oid, rpid: rpid, action: action);
|
||||||
// SmartDialog.dismiss();
|
// SmartDialog.dismiss();
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
@ -47,9 +47,12 @@ class _ZanButtonState extends State<ZanButton> {
|
|||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isProcessing = false;
|
bool isProcessing = false;
|
||||||
void Function()? handleState(Future Function() action) {
|
void Function()? handleState(Future Function() action) {
|
||||||
return isProcessing ? null : () async {
|
return isProcessing
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
setState(() => isProcessing = true);
|
setState(() => isProcessing = true);
|
||||||
await action();
|
await action();
|
||||||
setState(() => isProcessing = false);
|
setState(() => isProcessing = false);
|
||||||
@ -58,8 +61,9 @@ class _ZanButtonState extends State<ZanButton> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var color = Theme.of(context).colorScheme.outline;
|
final ThemeData t = Theme.of(context);
|
||||||
var primary = Theme.of(context).colorScheme.primary;
|
final Color color = t.colorScheme.outline;
|
||||||
|
final Color primary = t.colorScheme.primary;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 32,
|
height: 32,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
@ -79,12 +83,14 @@ class _ZanButtonState extends State<ZanButton> {
|
|||||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||||
return ScaleTransition(scale: animation, child: child);
|
return ScaleTransition(scale: animation, child: child);
|
||||||
},
|
},
|
||||||
child: Text(widget.replyItem!.like.toString(),
|
child: Text(
|
||||||
|
widget.replyItem!.like.toString(),
|
||||||
key: ValueKey<int>(widget.replyItem!.like!),
|
key: ValueKey<int>(widget.replyItem!.like!),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: widget.replyItem!.action == 1 ? primary : color,
|
color: widget.replyItem!.action == 1 ? primary : color,
|
||||||
fontSize:
|
fontSize: t.textTheme.labelSmall!.fontSize,
|
||||||
Theme.of(context).textTheme.labelSmall!.fontSize)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -31,14 +31,14 @@ class VideoReplyReplyController extends GetxController {
|
|||||||
currentPage = 0;
|
currentPage = 0;
|
||||||
}
|
}
|
||||||
isLoadingMore = true;
|
isLoadingMore = true;
|
||||||
var res = await ReplyHttp.replyReplyList(
|
final res = await ReplyHttp.replyReplyList(
|
||||||
oid: aid!,
|
oid: aid!,
|
||||||
root: rpid!,
|
root: rpid!,
|
||||||
pageNum: currentPage + 1,
|
pageNum: currentPage + 1,
|
||||||
type: replyType.index,
|
type: replyType.index,
|
||||||
);
|
);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
List<ReplyItemModel> replies = res['data'].replies;
|
final List<ReplyItemModel> replies = res['data'].replies;
|
||||||
if (replies.isNotEmpty) {
|
if (replies.isNotEmpty) {
|
||||||
noMore.value = '加载中...';
|
noMore.value = '加载中...';
|
||||||
if (replyList.length == res['data'].page.count) {
|
if (replyList.length == res['data'].page.count) {
|
||||||
@ -12,13 +12,6 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
class VideoReplyReplyPanel extends StatefulWidget {
|
class VideoReplyReplyPanel extends StatefulWidget {
|
||||||
final int? oid;
|
|
||||||
final int? rpid;
|
|
||||||
final Function? closePanel;
|
|
||||||
final ReplyItemModel? firstFloor;
|
|
||||||
final String? source;
|
|
||||||
final ReplyType? replyType;
|
|
||||||
|
|
||||||
const VideoReplyReplyPanel({
|
const VideoReplyReplyPanel({
|
||||||
this.oid,
|
this.oid,
|
||||||
this.rpid,
|
this.rpid,
|
||||||
@ -28,6 +21,12 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
|||||||
this.replyType,
|
this.replyType,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
final int? oid;
|
||||||
|
final int? rpid;
|
||||||
|
final Function? closePanel;
|
||||||
|
final ReplyItemModel? firstFloor;
|
||||||
|
final String? source;
|
||||||
|
final ReplyType? replyType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
|
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
|
||||||
@ -36,7 +35,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
|||||||
class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||||
late VideoReplyReplyController _videoReplyReplyController;
|
late VideoReplyReplyController _videoReplyReplyController;
|
||||||
late AnimationController replyAnimationCtl;
|
late AnimationController replyAnimationCtl;
|
||||||
Box localCache = GStrorage.localCache;
|
final Box<dynamic> localCache = GStrorage.localCache;
|
||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
late ScrollController scrollController;
|
late ScrollController scrollController;
|
||||||
@ -87,7 +86,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
padding: const EdgeInsets.only(left: 12, right: 2),
|
padding: const EdgeInsets.only(left: 12, right: 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Text('评论详情'),
|
const Text('评论详情'),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.close, size: 20),
|
icon: const Icon(Icons.close, size: 20),
|
||||||
@ -138,15 +137,15 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
],
|
],
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data as Map;
|
final Map data = snapshot.data as Map;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
// 请求成功
|
// 请求成功
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverList(
|
() => SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(BuildContext context, int index) {
|
||||||
if (index ==
|
if (index ==
|
||||||
_videoReplyReplyController
|
_videoReplyReplyController
|
||||||
.replyList.length) {
|
.replyList.length) {
|
||||||
@ -204,8 +203,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverList(
|
return SliverList(
|
||||||
delegate:
|
delegate: SliverChildBuilderDelegate(
|
||||||
SliverChildBuilderDelegate((context, index) {
|
(BuildContext context, int index) {
|
||||||
return const VideoReplySkeleton();
|
return const VideoReplySkeleton();
|
||||||
}, childCount: 8),
|
}, childCount: 8),
|
||||||
);
|
);
|
||||||
@ -8,6 +8,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:nil/nil.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
@ -47,16 +48,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
PlayerStatus playerStatus = PlayerStatus.playing;
|
PlayerStatus playerStatus = PlayerStatus.playing;
|
||||||
double doubleOffset = 0;
|
double doubleOffset = 0;
|
||||||
|
|
||||||
Box localCache = GStrorage.localCache;
|
final Box<dynamic> localCache = GStrorage.localCache;
|
||||||
Box setting = GStrorage.setting;
|
final Box<dynamic> setting = GStrorage.setting;
|
||||||
late double statusBarHeight;
|
late double statusBarHeight;
|
||||||
final videoHeight = Get.size.width * 9 / 16;
|
final double videoHeight = Get.size.width * 9 / 16;
|
||||||
late Future _futureBuilderFuture;
|
late Future _futureBuilderFuture;
|
||||||
// 自动退出全屏
|
// 自动退出全屏
|
||||||
late bool autoExitFullcreen;
|
late bool autoExitFullcreen;
|
||||||
late bool autoPlayEnable;
|
late bool autoPlayEnable;
|
||||||
late bool autoPiP;
|
late bool autoPiP;
|
||||||
final floating = Floating();
|
final Floating floating = Floating();
|
||||||
// 生命周期监听
|
// 生命周期监听
|
||||||
late final AppLifecycleListener _lifecycleListener;
|
late final AppLifecycleListener _lifecycleListener;
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
appbarStream = StreamController<double>();
|
appbarStream = StreamController<double>();
|
||||||
_extendNestCtr.addListener(
|
_extendNestCtr.addListener(
|
||||||
() {
|
() {
|
||||||
double offset = _extendNestCtr.position.pixels;
|
final double offset = _extendNestCtr.position.pixels;
|
||||||
appbarStream.add(offset);
|
appbarStream.add(offset);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -202,7 +203,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
// 离开当前页面时
|
// 离开当前页面时
|
||||||
void didPushNext() async {
|
void didPushNext() async {
|
||||||
/// 开启
|
/// 开启
|
||||||
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)) {
|
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)
|
||||||
|
as bool) {
|
||||||
videoDetailController.brightness = plPlayerController!.brightness.value;
|
videoDetailController.brightness = plPlayerController!.brightness.value;
|
||||||
}
|
}
|
||||||
if (plPlayerController != null) {
|
if (plPlayerController != null) {
|
||||||
@ -218,7 +220,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
// 返回当前页面时
|
// 返回当前页面时
|
||||||
void didPopNext() async {
|
void didPopNext() async {
|
||||||
videoDetailController.isFirstTime = false;
|
videoDetailController.isFirstTime = false;
|
||||||
bool autoplay = autoPlayEnable;
|
final bool autoplay = autoPlayEnable;
|
||||||
videoDetailController.playerInit(autoplay: autoplay);
|
videoDetailController.playerInit(autoplay: autoplay);
|
||||||
|
|
||||||
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
||||||
@ -238,7 +240,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
VideoDetailPage.routeObserver
|
VideoDetailPage.routeObserver
|
||||||
.subscribe(this, ModalRoute.of(context) as PageRoute);
|
.subscribe(this, ModalRoute.of(context)! as PageRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleTransition(String name) {
|
void _handleTransition(String name) {
|
||||||
@ -253,8 +255,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void autoEnterPip() {
|
void autoEnterPip() {
|
||||||
var routePath = Get.currentRoute;
|
final String routePath = Get.currentRoute;
|
||||||
bool isPortrait =
|
final bool isPortrait =
|
||||||
MediaQuery.of(context).orientation == Orientation.portrait;
|
MediaQuery.of(context).orientation == Orientation.portrait;
|
||||||
|
|
||||||
/// TODO 横屏全屏状态下误触pip
|
/// TODO 横屏全屏状态下误触pip
|
||||||
@ -269,7 +271,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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 =
|
final double pinnedHeaderHeight =
|
||||||
statusBarHeight + kToolbarHeight + videoHeight;
|
statusBarHeight + kToolbarHeight + videoHeight;
|
||||||
if (MediaQuery.of(context).orientation == Orientation.landscape ||
|
if (MediaQuery.of(context).orientation == Orientation.landscape ||
|
||||||
@ -303,19 +305,20 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
headerSliverBuilder:
|
headerSliverBuilder:
|
||||||
(BuildContext _context, bool innerBoxIsScrolled) {
|
(BuildContext _context, bool innerBoxIsScrolled) {
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
Obx(() => SliverAppBar(
|
Obx(
|
||||||
|
() => SliverAppBar(
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
// 假装使用一个非空变量,避免Obx检测不到而罢工
|
// 假装使用一个非空变量,避免Obx检测不到而罢工
|
||||||
pinned: videoDetailController
|
pinned: videoDetailController.autoPlay.value ^
|
||||||
.autoPlay.value ^ false ^ videoDetailController
|
false ^
|
||||||
.autoPlay.value,
|
videoDetailController.autoPlay.value,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
forceElevated: innerBoxIsScrolled,
|
forceElevated: innerBoxIsScrolled,
|
||||||
expandedHeight: MediaQuery.of(context).orientation ==
|
expandedHeight: MediaQuery.of(context).orientation ==
|
||||||
Orientation.landscape ||
|
Orientation.landscape ||
|
||||||
plPlayerController?.isFullScreen.value == true
|
plPlayerController?.isFullScreen.value == true
|
||||||
? MediaQuery.of(context).size.height -
|
? MediaQuery.sizeOf(context).height -
|
||||||
(MediaQuery.of(context).orientation ==
|
(MediaQuery.of(context).orientation ==
|
||||||
Orientation.landscape
|
Orientation.landscape
|
||||||
? 0
|
? 0
|
||||||
@ -338,14 +341,17 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (BuildContext context,
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
BoxConstraints boxConstraints) {
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
final double maxHeight =
|
||||||
|
boxConstraints.maxHeight;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: <Widget>[
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: ((context, snapshot) {
|
builder: (BuildContext context,
|
||||||
|
AsyncSnapshot snapshot) {
|
||||||
if (snapshot.hasData &&
|
if (snapshot.hasData &&
|
||||||
snapshot.data['status']) {
|
snapshot.data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
@ -378,7 +384,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
Obx(
|
Obx(
|
||||||
@ -418,6 +424,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
child: AppBar(
|
child: AppBar(
|
||||||
primary: false,
|
primary: false,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Colors.transparent,
|
Colors.transparent,
|
||||||
actions: [
|
actions: [
|
||||||
@ -468,7 +476,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
))),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
// pinnedHeaderSliverHeightBuilder: () {
|
// pinnedHeaderSliverHeightBuilder: () {
|
||||||
@ -479,11 +489,11 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
/// 不收回
|
/// 不收回
|
||||||
pinnedHeaderSliverHeightBuilder: () {
|
pinnedHeaderSliverHeightBuilder: () {
|
||||||
return plPlayerController?.isFullScreen.value == true
|
return plPlayerController?.isFullScreen.value == true
|
||||||
? MediaQuery.of(context).size.height
|
? MediaQuery.sizeOf(context).height
|
||||||
: pinnedHeaderHeight;
|
: pinnedHeaderHeight;
|
||||||
},
|
},
|
||||||
onlyOneScrollInBody: true,
|
onlyOneScrollInBody: true,
|
||||||
body: Container(
|
body: ColoredBox(
|
||||||
key: Key(heroTag),
|
key: Key(heroTag),
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -509,9 +519,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
controller: videoDetailController.tabCtr,
|
controller: videoDetailController.tabCtr,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
key: const PageStorageKey<String>('简介'),
|
key: const PageStorageKey<String>('简介'),
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
@ -544,7 +554,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
.withOpacity(0.06),
|
.withOpacity(0.06),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const RelatedVideoPanel(),
|
RelatedVideoPanel(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -581,7 +591,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
Widget childWhenEnabled = FutureBuilder(
|
Widget childWhenEnabled = FutureBuilder(
|
||||||
key: Key(heroTag),
|
key: Key(heroTag),
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: ((context, snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.hasData && snapshot.data['status']) {
|
if (snapshot.hasData && snapshot.data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => !videoDetailController.autoPlay.value
|
() => !videoDetailController.autoPlay.value
|
||||||
@ -603,9 +613,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return nil;
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
return PiPSwitcher(
|
return PiPSwitcher(
|
||||||
|
|||||||
@ -16,7 +16,7 @@ class ScrollAppBar extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
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(
|
return Positioned(
|
||||||
top: -videoHeight + scrollVal + kToolbarHeight + 0.5,
|
top: -videoHeight + scrollVal + kToolbarHeight + 0.5,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|||||||
@ -19,15 +19,15 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import 'package:pilipala/http/danmaku.dart';
|
import 'package:pilipala/http/danmaku.dart';
|
||||||
|
|
||||||
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||||
final PlPlayerController? controller;
|
|
||||||
final VideoDetailController? videoDetailCtr;
|
|
||||||
final Floating? floating;
|
|
||||||
const HeaderControl({
|
const HeaderControl({
|
||||||
this.controller,
|
this.controller,
|
||||||
this.videoDetailCtr,
|
this.videoDetailCtr,
|
||||||
this.floating,
|
this.floating,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
final PlPlayerController? controller;
|
||||||
|
final VideoDetailController? videoDetailCtr;
|
||||||
|
final Floating? floating;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<HeaderControl> createState() => _HeaderControlState();
|
State<HeaderControl> createState() => _HeaderControlState();
|
||||||
@ -42,9 +42,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
TextStyle subTitleStyle = const TextStyle(fontSize: 12);
|
TextStyle subTitleStyle = const TextStyle(fontSize: 12);
|
||||||
TextStyle titleStyle = const TextStyle(fontSize: 14);
|
TextStyle titleStyle = const TextStyle(fontSize: 14);
|
||||||
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
||||||
Box localCache = GStrorage.localCache;
|
final Box<dynamic> localCache = GStrorage.localCache;
|
||||||
Box videoStorage = GStrorage.video;
|
final Box<dynamic> videoStorage = GStrorage.video;
|
||||||
late List speedsList;
|
late List<double> speedsList;
|
||||||
double buttonSpace = 8;
|
double buttonSpace = 8;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -71,7 +71,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
),
|
),
|
||||||
margin: const EdgeInsets.all(12),
|
margin: const EdgeInsets.all(12),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 35,
|
height: 35,
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -118,7 +118,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// ),
|
// ),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
var res = await UserHttp.toViewLater(
|
final res = await UserHttp.toViewLater(
|
||||||
bvid: widget.videoDetailCtr!.bvid);
|
bvid: widget.videoDetailCtr!.bvid);
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
Get.back();
|
Get.back();
|
||||||
@ -187,11 +187,12 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
bool isSending = false; // 追踪是否正在发送
|
bool isSending = false; // 追踪是否正在发送
|
||||||
showDialog(
|
showDialog(
|
||||||
context: Get.context!,
|
context: Get.context!,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
// TODO: 支持更多类型和颜色的弹幕
|
// TODO: 支持更多类型和颜色的弹幕
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('发送弹幕(测试)'),
|
title: const Text('发送弹幕(测试)'),
|
||||||
content: StatefulBuilder(builder: (context, StateSetter setState) {
|
content: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
return TextField(
|
return TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
);
|
);
|
||||||
@ -204,12 +205,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
StatefulBuilder(builder: (context, StateSetter setState) {
|
StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
return TextButton(
|
return TextButton(
|
||||||
onPressed: isSending
|
onPressed: isSending
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
String msg = textController.text;
|
final String msg = textController.text;
|
||||||
if (msg.isEmpty) {
|
if (msg.isEmpty) {
|
||||||
SmartDialog.showToast('弹幕内容不能为空');
|
SmartDialog.showToast('弹幕内容不能为空');
|
||||||
return;
|
return;
|
||||||
@ -222,10 +224,10 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
});
|
});
|
||||||
//修改按钮文字
|
//修改按钮文字
|
||||||
// SmartDialog.showToast('弹幕发送中,\n$msg');
|
// SmartDialog.showToast('弹幕发送中,\n$msg');
|
||||||
dynamic res = await DanmakaHttp.shootDanmaku(
|
final dynamic res = await DanmakaHttp.shootDanmaku(
|
||||||
oid: widget.videoDetailCtr!.cid!.value,
|
oid: widget.videoDetailCtr!.cid.value,
|
||||||
msg: textController.text,
|
msg: textController.text,
|
||||||
bvid: widget.videoDetailCtr!.bvid!,
|
bvid: widget.videoDetailCtr!.bvid,
|
||||||
progress:
|
progress:
|
||||||
widget.controller!.position.value.inMilliseconds,
|
widget.controller!.position.value.inMilliseconds,
|
||||||
type: 1,
|
type: 1,
|
||||||
@ -262,20 +264,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
|
|
||||||
/// 选择倍速
|
/// 选择倍速
|
||||||
void showSetSpeedSheet() {
|
void showSetSpeedSheet() {
|
||||||
double currentSpeed = widget.controller!.playbackSpeed;
|
final double currentSpeed = widget.controller!.playbackSpeed;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: Get.context!,
|
context: Get.context!,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('播放速度'),
|
title: const Text('播放速度'),
|
||||||
content: StatefulBuilder(builder: (context, StateSetter setState) {
|
content: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
return Wrap(
|
return Wrap(
|
||||||
alignment: WrapAlignment.start,
|
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 2,
|
runSpacing: 2,
|
||||||
children: [
|
children: [
|
||||||
for (var i in speedsList) ...[
|
for (final double i in speedsList) ...<Widget>[
|
||||||
if (i == currentSpeed) ...[
|
if (i == currentSpeed) ...<Widget>[
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// setState(() => currentSpeed = i),
|
// setState(() => currentSpeed = i),
|
||||||
@ -298,7 +300,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
actions: [
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => SmartDialog.dismiss(),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -321,18 +323,18 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
|
|
||||||
/// 选择画质
|
/// 选择画质
|
||||||
void showSetVideoQa() {
|
void showSetVideoQa() {
|
||||||
List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||||
VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
final VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
||||||
|
|
||||||
/// 总质量分类
|
/// 总质量分类
|
||||||
int totalQaSam = videoFormat.length;
|
final int totalQaSam = videoFormat.length;
|
||||||
|
|
||||||
/// 可用的质量分类
|
/// 可用的质量分类
|
||||||
int userfulQaSam = 0;
|
int userfulQaSam = 0;
|
||||||
List<VideoItem> video = videoInfo.dash!.video!;
|
final List<VideoItem> video = videoInfo.dash!.video!;
|
||||||
Set<int> idSet = {};
|
final Set<int> idSet = {};
|
||||||
for (var item in video) {
|
for (final VideoItem item in video) {
|
||||||
int id = item.id!;
|
final int id = item.id!;
|
||||||
if (!idSet.contains(id)) {
|
if (!idSet.contains(id)) {
|
||||||
idSet.add(id);
|
idSet.add(id);
|
||||||
userfulQaSam++;
|
userfulQaSam++;
|
||||||
@ -354,7 +356,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
),
|
),
|
||||||
margin: const EdgeInsets.all(12),
|
margin: const EdgeInsets.all(12),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 45,
|
height: 45,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -380,7 +382,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
for (var i = 0; i < totalQaSam; i++) ...[
|
for (int i = 0; i < totalQaSam; i++) ...[
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (currentVideoQa.code ==
|
if (currentVideoQa.code ==
|
||||||
@ -427,9 +429,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
|
|
||||||
/// 选择音质
|
/// 选择音质
|
||||||
void showSetAudioQa() {
|
void showSetAudioQa() {
|
||||||
AudioQuality currentAudioQa = widget.videoDetailCtr!.currentAudioQa!;
|
final AudioQuality currentAudioQa = widget.videoDetailCtr!.currentAudioQa!;
|
||||||
|
final List<AudioItem> audio = videoInfo.dash!.audio!;
|
||||||
List<AudioItem> audio = videoInfo.dash!.audio!;
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
@ -445,18 +446,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
),
|
),
|
||||||
margin: const EdgeInsets.all(12),
|
margin: const EdgeInsets.all(12),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 45,
|
height: 45,
|
||||||
child: Center(child: Text('选择音质', style: titleStyle))),
|
child: Center(child: Text('选择音质', style: titleStyle))),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Material(
|
child: Material(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: <Widget>[
|
||||||
for (var i in audio) ...[
|
for (final AudioItem i in audio) ...<Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (currentAudioQa.code == i.id) return;
|
if (currentAudioQa.code == i.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final int quality = i.id!;
|
final int quality = i.id!;
|
||||||
widget.videoDetailCtr!.currentAudioQa =
|
widget.videoDetailCtr!.currentAudioQa =
|
||||||
AudioQualityCode.fromCode(quality)!;
|
AudioQualityCode.fromCode(quality)!;
|
||||||
@ -493,13 +496,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// 选择解码格式
|
// 选择解码格式
|
||||||
void showSetDecodeFormats() {
|
void showSetDecodeFormats() {
|
||||||
// 当前选中的解码格式
|
// 当前选中的解码格式
|
||||||
VideoDecodeFormats currentDecodeFormats =
|
final VideoDecodeFormats currentDecodeFormats =
|
||||||
widget.videoDetailCtr!.currentDecodeFormats;
|
widget.videoDetailCtr!.currentDecodeFormats;
|
||||||
VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
||||||
// 当前视频可用的解码格式
|
// 当前视频可用的解码格式
|
||||||
List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||||
List list = videoFormat
|
final List list = videoFormat
|
||||||
.firstWhere((e) => e.quality == firstVideo.quality!.code)
|
.firstWhere((FormatItem e) => e.quality == firstVideo.quality!.code)
|
||||||
.codecs!;
|
.codecs!;
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@ -565,15 +568,15 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
/// 弹幕功能
|
/// 弹幕功能
|
||||||
void showSetDanmaku() async {
|
void showSetDanmaku() async {
|
||||||
// 屏蔽类型
|
// 屏蔽类型
|
||||||
List<Map<String, dynamic>> blockTypesList = [
|
final List<Map<String, dynamic>> blockTypesList = [
|
||||||
{'value': 5, 'label': '顶部'},
|
{'value': 5, 'label': '顶部'},
|
||||||
{'value': 2, 'label': '滚动'},
|
{'value': 2, 'label': '滚动'},
|
||||||
{'value': 4, 'label': '底部'},
|
{'value': 4, 'label': '底部'},
|
||||||
{'value': 6, '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.25, 'label': '1/4屏'},
|
||||||
{'value': 0.5, 'label': '半屏'},
|
{'value': 0.5, 'label': '半屏'},
|
||||||
{'value': 0.75, 'label': '3/4屏'},
|
{'value': 0.75, 'label': '3/4屏'},
|
||||||
@ -587,13 +590,15 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// 弹幕速度
|
// 弹幕速度
|
||||||
double danmakuDurationVal = widget.controller!.danmakuDurationVal;
|
double danmakuDurationVal = widget.controller!.danmakuDurationVal;
|
||||||
|
|
||||||
DanmakuController danmakuController = widget.controller!.danmakuController!;
|
final DanmakuController danmakuController =
|
||||||
|
widget.controller!.danmakuController!;
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return StatefulBuilder(builder: (context, StateSetter setState) {
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 580,
|
height: 580,
|
||||||
@ -617,11 +622,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 12, bottom: 18),
|
padding: const EdgeInsets.only(top: 12, bottom: 18),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
for (var i in blockTypesList) ...[
|
for (final Map<String, dynamic> i
|
||||||
|
in blockTypesList) ...<Widget>[
|
||||||
ActionRowLineItem(
|
ActionRowLineItem(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
bool isChoose = blockTypes.contains(i['value']);
|
final bool isChoose =
|
||||||
|
blockTypes.contains(i['value']);
|
||||||
if (isChoose) {
|
if (isChoose) {
|
||||||
blockTypes.remove(i['value']);
|
blockTypes.remove(i['value']);
|
||||||
} else {
|
} else {
|
||||||
@ -630,9 +637,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
widget.controller!.blockTypes = blockTypes;
|
widget.controller!.blockTypes = blockTypes;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
try {
|
try {
|
||||||
DanmakuOption currentOption =
|
final DanmakuOption currentOption =
|
||||||
danmakuController.option;
|
danmakuController.option;
|
||||||
DanmakuOption updatedOption =
|
final DanmakuOption updatedOption =
|
||||||
currentOption.copyWith(
|
currentOption.copyWith(
|
||||||
hideTop: blockTypes.contains(5),
|
hideTop: blockTypes.contains(5),
|
||||||
hideBottom: blockTypes.contains(4),
|
hideBottom: blockTypes.contains(4),
|
||||||
@ -655,16 +662,16 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
padding: const EdgeInsets.only(top: 12, bottom: 18),
|
padding: const EdgeInsets.only(top: 12, bottom: 18),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
for (var i in showAreas) ...[
|
for (final Map<String, dynamic> i in showAreas) ...[
|
||||||
ActionRowLineItem(
|
ActionRowLineItem(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showArea = i['value'];
|
showArea = i['value'];
|
||||||
widget.controller!.showArea = showArea;
|
widget.controller!.showArea = showArea;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
try {
|
try {
|
||||||
DanmakuOption currentOption =
|
final DanmakuOption currentOption =
|
||||||
danmakuController.option;
|
danmakuController.option;
|
||||||
DanmakuOption updatedOption =
|
final DanmakuOption updatedOption =
|
||||||
currentOption.copyWith(area: i['value']);
|
currentOption.copyWith(area: i['value']);
|
||||||
danmakuController.updateOption(updatedOption);
|
danmakuController.updateOption(updatedOption);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -705,9 +712,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
widget.controller!.opacityVal = opacityVal;
|
widget.controller!.opacityVal = opacityVal;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
try {
|
try {
|
||||||
DanmakuOption currentOption =
|
final DanmakuOption currentOption =
|
||||||
danmakuController.option;
|
danmakuController.option;
|
||||||
DanmakuOption updatedOption =
|
final DanmakuOption updatedOption =
|
||||||
currentOption.copyWith(opacity: val);
|
currentOption.copyWith(opacity: val);
|
||||||
danmakuController.updateOption(updatedOption);
|
danmakuController.updateOption(updatedOption);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -743,9 +750,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
widget.controller!.fontSizeVal = fontSizeVal;
|
widget.controller!.fontSizeVal = fontSizeVal;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
try {
|
try {
|
||||||
DanmakuOption currentOption =
|
final DanmakuOption currentOption =
|
||||||
danmakuController.option;
|
danmakuController.option;
|
||||||
DanmakuOption updatedOption =
|
final DanmakuOption updatedOption =
|
||||||
currentOption.copyWith(
|
currentOption.copyWith(
|
||||||
fontSize: (15 * fontSizeVal).toDouble(),
|
fontSize: (15 * fontSizeVal).toDouble(),
|
||||||
);
|
);
|
||||||
@ -780,13 +787,15 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
label: danmakuDurationVal.toString(),
|
label: danmakuDurationVal.toString(),
|
||||||
onChanged: (double val) {
|
onChanged: (double val) {
|
||||||
danmakuDurationVal = val;
|
danmakuDurationVal = val;
|
||||||
widget.controller!.danmakuDurationVal = danmakuDurationVal;
|
widget.controller!.danmakuDurationVal =
|
||||||
|
danmakuDurationVal;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
try {
|
try {
|
||||||
DanmakuOption currentOption =
|
final DanmakuOption currentOption =
|
||||||
danmakuController.option;
|
danmakuController.option;
|
||||||
DanmakuOption updatedOption =
|
final DanmakuOption updatedOption =
|
||||||
currentOption.copyWith(duration:
|
currentOption.copyWith(
|
||||||
|
duration:
|
||||||
val / widget.controller!.playbackSpeed);
|
val / widget.controller!.playbackSpeed);
|
||||||
danmakuController.updateOption(updatedOption);
|
danmakuController.updateOption(updatedOption);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -827,8 +836,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Material(
|
child: Material(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: <Widget>[
|
||||||
for (var i in PlayRepeat.values) ...[
|
for (final PlayRepeat i in PlayRepeat.values) ...[
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
widget.controller!.setPlayRepeat(i);
|
widget.controller!.setPlayRepeat(i);
|
||||||
@ -860,7 +869,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final _ = widget.controller!;
|
final _ = widget.controller!;
|
||||||
const textStyle = TextStyle(
|
const TextStyle textStyle = TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
);
|
);
|
||||||
@ -881,11 +890,14 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
size: 15,
|
size: 15,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
fuc: () => {
|
fuc: () => <Set<void>>{
|
||||||
if (widget.controller!.isFullScreen.value){
|
if (widget.controller!.isFullScreen.value)
|
||||||
widget.controller!.triggerFullScreen(status: false)
|
<void>{widget.controller!.triggerFullScreen(status: false)}
|
||||||
} else {
|
else
|
||||||
if (MediaQuery.of(context).orientation == Orientation.landscape){
|
<void>{
|
||||||
|
if (MediaQuery.of(context).orientation ==
|
||||||
|
Orientation.landscape)
|
||||||
|
{
|
||||||
SystemChrome.setPreferredOrientations([
|
SystemChrome.setPreferredOrientations([
|
||||||
DeviceOrientation.portraitUp,
|
DeviceOrientation.portraitUp,
|
||||||
])
|
])
|
||||||
@ -905,7 +917,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// 销毁播放器实例
|
// 销毁播放器实例
|
||||||
await widget.controller!.dispose(type: 'all');
|
await widget.controller!.dispose(type: 'all');
|
||||||
if (mounted) {
|
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),
|
SizedBox(width: buttonSpace),
|
||||||
if (Platform.isAndroid) ...[
|
if (Platform.isAndroid) ...<Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
@ -971,7 +984,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
canUsePiP = false;
|
canUsePiP = false;
|
||||||
}
|
}
|
||||||
if (canUsePiP) {
|
if (canUsePiP) {
|
||||||
final aspectRatio = Rational(
|
final Rational aspectRatio = Rational(
|
||||||
widget.videoDetailCtr!.data.dash!.video!.first.width!,
|
widget.videoDetailCtr!.data.dash!.video!.first.width!,
|
||||||
widget.videoDetailCtr!.data.dash!.video!.first.height!,
|
widget.videoDetailCtr!.data.dash!.video!.first.height!,
|
||||||
);
|
);
|
||||||
@ -997,7 +1010,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
),
|
),
|
||||||
onPressed: () => showSetSpeedSheet(),
|
onPressed: () => showSetSpeedSheet(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${_.playbackSpeed.toString()}X',
|
'${_.playbackSpeed}X',
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import 'package:pilipala/http/init.dart';
|
||||||
import 'package:pilipala/http/member.dart';
|
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/media/index.dart';
|
import 'package:pilipala/pages/media/index.dart';
|
||||||
@ -53,9 +52,9 @@ class WebviewController extends GetxController {
|
|||||||
loadProgress.value = progress;
|
loadProgress.value = progress;
|
||||||
},
|
},
|
||||||
onPageStarted: (String url) {
|
onPageStarted: (String url) {
|
||||||
String str = Uri.parse(url).pathSegments[0];
|
final String str = Uri.parse(url).pathSegments[0];
|
||||||
Map matchRes = IdUtils.matchAvorBv(input: str);
|
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||||
List matchKeys = matchRes.keys.toList();
|
final List matchKeys = matchRes.keys.toList();
|
||||||
if (matchKeys.isNotEmpty) {
|
if (matchKeys.isNotEmpty) {
|
||||||
if (matchKeys.first == 'BV') {
|
if (matchKeys.first == 'BV') {
|
||||||
Get.offAndToNamed(
|
Get.offAndToNamed(
|
||||||
@ -102,21 +101,21 @@ class WebviewController extends GetxController {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await SetCookie.onSet();
|
await SetCookie.onSet();
|
||||||
var result = await UserHttp.userInfo();
|
final result = await UserHttp.userInfo();
|
||||||
if (result['status'] && result['data'].isLogin) {
|
if (result['status'] && result['data'].isLogin) {
|
||||||
SmartDialog.showToast('登录成功');
|
SmartDialog.showToast('登录成功');
|
||||||
try {
|
try {
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
await userInfoCache.put('userInfoCache', result['data']);
|
await userInfoCache.put('userInfoCache', result['data']);
|
||||||
|
|
||||||
HomeController homeCtr = Get.find<HomeController>();
|
final HomeController homeCtr = Get.find<HomeController>();
|
||||||
homeCtr.updateLoginStatus(true);
|
homeCtr.updateLoginStatus(true);
|
||||||
homeCtr.userFace.value = result['data'].face;
|
homeCtr.userFace.value = result['data'].face;
|
||||||
MediaController mediaCtr = Get.find<MediaController>();
|
final MediaController mediaCtr = Get.find<MediaController>();
|
||||||
mediaCtr.mid = result['data'].mid;
|
mediaCtr.mid = result['data'].mid;
|
||||||
await LoginUtils.refreshLoginStatus(true);
|
await LoginUtils.refreshLoginStatus(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
SmartDialog.show(builder: (context) {
|
SmartDialog.show(builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('登录遇到问题'),
|
title: const Text('登录遇到问题'),
|
||||||
content: Text(err.toString()),
|
content: Text(err.toString()),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/pages/whisperDetail/controller.dart';
|
import 'package:pilipala/pages/whisper_detail/controller.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
|
||||||
import 'widget/chat_item.dart';
|
import 'widget/chat_item.dart';
|
||||||
@ -41,8 +41,8 @@ class _WhisperDetailPageState extends State<WhisperDetailPage> {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
backgroundColor:
|
backgroundColor: MaterialStateProperty.resolveWith(
|
||||||
MaterialStateProperty.resolveWith((states) {
|
(Set<MaterialState> states) {
|
||||||
return Theme.of(context)
|
return Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primaryContainer
|
.primaryContainer
|
||||||
@ -69,7 +69,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
NetworkImgLayer(
|
NetworkImgLayer(
|
||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
@ -91,12 +91,12 @@ class _WhisperDetailPageState extends State<WhisperDetailPage> {
|
|||||||
),
|
),
|
||||||
body: FutureBuilder(
|
body: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
Map data = snapshot.data as Map;
|
final Map data = snapshot.data as Map;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
List messageList = _whisperDetailController.messageList;
|
List messageList = _whisperDetailController.messageList;
|
||||||
return Obx(
|
return Obx(
|
||||||
@ -15,11 +15,12 @@ class ChatItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isOwner = item.senderUid == GStrorage.userInfo.get('userInfoCache').mid;
|
bool isOwner =
|
||||||
|
item.senderUid == GStrorage.userInfo.get('userInfoCache').mid;
|
||||||
bool isPic = item.msgType == 2; // 图片
|
bool isPic = item.msgType == 2; // 图片
|
||||||
bool isText = item.msgType == 1; // 文本
|
bool isText = item.msgType == 1; // 文本
|
||||||
bool isAchive = item.msgType == 11; // 投稿
|
// bool isAchive = item.msgType == 11; // 投稿
|
||||||
bool isArticle = item.msgType == 12; // 专栏
|
// bool isArticle = item.msgType == 12; // 专栏
|
||||||
bool isRevoke = item.msgType == 5; // 撤回消息
|
bool isRevoke = item.msgType == 5; // 撤回消息
|
||||||
|
|
||||||
bool isSystem =
|
bool isSystem =
|
||||||
@ -222,7 +222,7 @@ class PlPlayerController {
|
|||||||
late double opacityVal;
|
late double opacityVal;
|
||||||
late double fontSizeVal;
|
late double fontSizeVal;
|
||||||
late double danmakuDurationVal;
|
late double danmakuDurationVal;
|
||||||
late List speedsList;
|
late List<double> speedsList;
|
||||||
// 缓存
|
// 缓存
|
||||||
double? defaultDuration;
|
double? defaultDuration;
|
||||||
late bool enableAutoLongPressSpeed = false;
|
late bool enableAutoLongPressSpeed = false;
|
||||||
@ -236,24 +236,28 @@ class PlPlayerController {
|
|||||||
sliderPositionSeconds.value = newSecond;
|
sliderPositionSeconds.value = newSecond;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePositionSecond() {
|
void updatePositionSecond() {
|
||||||
int newSecond = _position.value.inSeconds;
|
int newSecond = _position.value.inSeconds;
|
||||||
if (positionSeconds.value != newSecond) {
|
if (positionSeconds.value != newSecond) {
|
||||||
positionSeconds.value = newSecond;
|
positionSeconds.value = newSecond;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDurationSecond() {
|
void updateDurationSecond() {
|
||||||
int newSecond = _duration.value.inSeconds;
|
int newSecond = _duration.value.inSeconds;
|
||||||
if (durationSeconds.value != newSecond) {
|
if (durationSeconds.value != newSecond) {
|
||||||
durationSeconds.value = newSecond;
|
durationSeconds.value = newSecond;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateBufferedSecond() {
|
void updateBufferedSecond() {
|
||||||
int newSecond = _buffered.value.inSeconds;
|
int newSecond = _buffered.value.inSeconds;
|
||||||
if (bufferedSeconds.value != newSecond) {
|
if (bufferedSeconds.value != newSecond) {
|
||||||
bufferedSeconds.value = newSecond;
|
bufferedSeconds.value = newSecond;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加一个私有构造函数
|
// 添加一个私有构造函数
|
||||||
PlPlayerController._() {
|
PlPlayerController._() {
|
||||||
_videoType = videoType;
|
_videoType = videoType;
|
||||||
@ -285,10 +289,10 @@ class PlPlayerController {
|
|||||||
_longPressSpeed.value = videoStorage
|
_longPressSpeed.value = videoStorage
|
||||||
.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0);
|
.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0);
|
||||||
}
|
}
|
||||||
List speedsListTemp =
|
final List<double> speedsListTemp = videoStorage
|
||||||
videoStorage.get(VideoBoxKey.customSpeedsList, defaultValue: []);
|
.get(VideoBoxKey.customSpeedsList, defaultValue: <double>[]);
|
||||||
speedsList = List.from(speedsListTemp);
|
speedsList = List<double>.from(speedsListTemp);
|
||||||
for (var i in PlaySpeed.values) {
|
for (final PlaySpeed i in PlaySpeed.values) {
|
||||||
speedsList.add(i.value);
|
speedsList.add(i.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,6 +941,7 @@ class PlPlayerController {
|
|||||||
if (!isFullScreen.value && status) {
|
if (!isFullScreen.value && status) {
|
||||||
/// 按照视频宽高比决定全屏方向
|
/// 按照视频宽高比决定全屏方向
|
||||||
toggleFullScreen(true);
|
toggleFullScreen(true);
|
||||||
|
|
||||||
/// 进入全屏
|
/// 进入全屏
|
||||||
await enterFullScreen();
|
await enterFullScreen();
|
||||||
if (mode == FullScreenMode.vertical ||
|
if (mode == FullScreenMode.vertical ||
|
||||||
|
|||||||
@ -6,13 +6,13 @@ enum PlayRepeat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension PlayRepeatExtension on PlayRepeat {
|
extension PlayRepeatExtension on PlayRepeat {
|
||||||
static final List<String> _descList = [
|
static final List<String> _descList = <String>[
|
||||||
'播完暂停',
|
'播完暂停',
|
||||||
'顺序播放',
|
'顺序播放',
|
||||||
'单个循环',
|
'单个循环',
|
||||||
'列表循环',
|
'列表循环',
|
||||||
];
|
];
|
||||||
get description => _descList[index];
|
String get description => _descList[index];
|
||||||
|
|
||||||
static final List<double> _valueList = [
|
static final List<double> _valueList = [
|
||||||
1,
|
1,
|
||||||
@ -20,6 +20,6 @@ extension PlayRepeatExtension on PlayRepeat {
|
|||||||
3,
|
3,
|
||||||
4,
|
4,
|
||||||
];
|
];
|
||||||
get value => _valueList[index];
|
double get value => _valueList[index];
|
||||||
get defaultValue => _valueList[1];
|
double get defaultValue => _valueList[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ extension PlaySpeedExtension on PlaySpeed {
|
|||||||
'1.75',
|
'1.75',
|
||||||
'2.0',
|
'2.0',
|
||||||
];
|
];
|
||||||
get description => _descList[index];
|
String get description => _descList[index];
|
||||||
|
|
||||||
static final List<double> _valueList = [
|
static final List<double> _valueList = [
|
||||||
0.25,
|
0.25,
|
||||||
@ -34,6 +34,6 @@ extension PlaySpeedExtension on PlaySpeed {
|
|||||||
1.75,
|
1.75,
|
||||||
2.0,
|
2.0,
|
||||||
];
|
];
|
||||||
get value => _valueList[index];
|
double get value => _valueList[index];
|
||||||
get defaultValue => _valueList[3];
|
double get defaultValue => _valueList[3];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,30 +9,23 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
|
import 'package:nil/nil.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/controller.dart';
|
import 'package:pilipala/plugin/pl_player/controller.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/models/duration.dart';
|
import 'package:pilipala/plugin/pl_player/models/duration.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/models/fullscreen_mode.dart';
|
import 'package:pilipala/plugin/pl_player/models/fullscreen_mode.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/models/play_status.dart';
|
|
||||||
import 'package:pilipala/plugin/pl_player/utils.dart';
|
import 'package:pilipala/plugin/pl_player/utils.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:screen_brightness/screen_brightness.dart';
|
import 'package:screen_brightness/screen_brightness.dart';
|
||||||
|
|
||||||
import 'models/bottom_progress_behavior.dart';
|
import 'models/bottom_progress_behavior.dart';
|
||||||
import 'utils/fullscreen.dart';
|
|
||||||
import 'widgets/app_bar_ani.dart';
|
import 'widgets/app_bar_ani.dart';
|
||||||
import 'widgets/backward_seek.dart';
|
import 'widgets/backward_seek.dart';
|
||||||
import 'widgets/bottom_control.dart';
|
import 'widgets/bottom_control.dart';
|
||||||
import 'widgets/common_btn.dart';
|
import 'widgets/common_btn.dart';
|
||||||
import 'widgets/forward_seek.dart';
|
import 'widgets/forward_seek.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
|
|
||||||
class PLVideoPlayer extends StatefulWidget {
|
class PLVideoPlayer extends StatefulWidget {
|
||||||
final PlPlayerController controller;
|
|
||||||
final PreferredSizeWidget? headerControl;
|
|
||||||
final PreferredSizeWidget? bottomControl;
|
|
||||||
final Widget? danmuWidget;
|
|
||||||
|
|
||||||
const PLVideoPlayer({
|
const PLVideoPlayer({
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.headerControl,
|
this.headerControl,
|
||||||
@ -41,6 +34,11 @@ class PLVideoPlayer extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final PlPlayerController controller;
|
||||||
|
final PreferredSizeWidget? headerControl;
|
||||||
|
final PreferredSizeWidget? bottomControl;
|
||||||
|
final Widget? danmuWidget;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
||||||
}
|
}
|
||||||
@ -66,7 +64,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
|
|
||||||
double _distance = 0.0;
|
double _distance = 0.0;
|
||||||
// 初始手指落下位置
|
// 初始手指落下位置
|
||||||
double _initTapPositoin = 0.0;
|
// double _initTapPositoin = 0.0;
|
||||||
|
|
||||||
// bool _volumeInterceptEventStream = false;
|
// bool _volumeInterceptEventStream = false;
|
||||||
|
|
||||||
@ -90,7 +88,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
|
|
||||||
// 双击播放、暂停
|
// 双击播放、暂停
|
||||||
void onDoubleTapCenter() {
|
void onDoubleTapCenter() {
|
||||||
final _ = widget.controller;
|
final PlPlayerController _ = widget.controller;
|
||||||
if (_.videoPlayerController!.state.playing) {
|
if (_.videoPlayerController!.state.playing) {
|
||||||
_.pause();
|
_.pause();
|
||||||
} else {
|
} else {
|
||||||
@ -98,7 +96,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doubleTapFuc(String type) {
|
void doubleTapFuc(String type) {
|
||||||
if (!enableQuickDouble) {
|
if (!enableQuickDouble) {
|
||||||
onDoubleTapCenter();
|
onDoubleTapCenter();
|
||||||
return;
|
return;
|
||||||
@ -138,7 +136,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
try {
|
try {
|
||||||
FlutterVolumeController.showSystemUI = true;
|
FlutterVolumeController.showSystemUI = true;
|
||||||
_ctr.volumeValue.value = (await FlutterVolumeController.getVolume())!;
|
_ctr.volumeValue.value = (await FlutterVolumeController.getVolume())!;
|
||||||
FlutterVolumeController.addListener((value) {
|
FlutterVolumeController.addListener((double value) {
|
||||||
if (mounted && !_ctr.volumeInterceptEventStream.value) {
|
if (mounted && !_ctr.volumeInterceptEventStream.value) {
|
||||||
_ctr.volumeValue.value = value;
|
_ctr.volumeValue.value = value;
|
||||||
}
|
}
|
||||||
@ -149,7 +147,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
Future.microtask(() async {
|
Future.microtask(() async {
|
||||||
try {
|
try {
|
||||||
_ctr.brightnessValue.value = await ScreenBrightness().current;
|
_ctr.brightnessValue.value = await ScreenBrightness().current;
|
||||||
ScreenBrightness().onCurrentBrightnessChanged.listen((value) {
|
ScreenBrightness().onCurrentBrightnessChanged.listen((double value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_ctr.brightnessValue.value = value;
|
_ctr.brightnessValue.value = value;
|
||||||
}
|
}
|
||||||
@ -198,9 +196,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final _ = widget.controller;
|
final PlPlayerController _ = widget.controller;
|
||||||
Color colorTheme = Theme.of(context).colorScheme.primary;
|
final Color colorTheme = Theme.of(context).colorScheme.primary;
|
||||||
TextStyle subTitleStyle = const TextStyle(
|
const TextStyle subTitleStyle = TextStyle(
|
||||||
height: 1.5,
|
height: 1.5,
|
||||||
fontSize: 40.0,
|
fontSize: 40.0,
|
||||||
letterSpacing: 0.0,
|
letterSpacing: 0.0,
|
||||||
@ -209,24 +207,22 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
backgroundColor: Color(0xaa000000),
|
backgroundColor: Color(0xaa000000),
|
||||||
);
|
);
|
||||||
const textStyle = TextStyle(
|
const TextStyle textStyle = TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
);
|
);
|
||||||
return Stack(
|
return Stack(
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
fit: StackFit.passthrough,
|
fit: StackFit.passthrough,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Obx(
|
Obx(
|
||||||
() => Video(
|
() => Video(
|
||||||
controller: videoController,
|
controller: videoController,
|
||||||
controls: NoVideoControls,
|
controls: NoVideoControls,
|
||||||
pauseUponEnteringBackgroundMode: !enableBackgroundPlay,
|
pauseUponEnteringBackgroundMode: !enableBackgroundPlay,
|
||||||
resumeUponEnteringForegroundMode: true,
|
resumeUponEnteringForegroundMode: true,
|
||||||
subtitleViewConfiguration: SubtitleViewConfiguration(
|
subtitleViewConfiguration: const SubtitleViewConfiguration(
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
textAlign: TextAlign.center,
|
padding: EdgeInsets.all(24.0),
|
||||||
padding: const EdgeInsets.all(24.0),
|
|
||||||
),
|
),
|
||||||
fit: _.videoFit.value,
|
fit: _.videoFit.value,
|
||||||
),
|
),
|
||||||
@ -315,7 +311,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
/// 音量🔊 控制条展示
|
/// 音量🔊 控制条展示
|
||||||
Obx(
|
Obx(
|
||||||
() => Align(
|
() => Align(
|
||||||
alignment: Alignment.center,
|
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
opacity: _ctr.volumeIndicator.value ? 1.0 : 0.0,
|
opacity: _ctr.volumeIndicator.value ? 1.0 : 0.0,
|
||||||
@ -331,8 +326,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: <Widget>[
|
||||||
children: [
|
|
||||||
Container(
|
Container(
|
||||||
height: 34.0,
|
height: 34.0,
|
||||||
width: 28.0,
|
width: 28.0,
|
||||||
@ -368,7 +362,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
/// 亮度🌞 控制条展示
|
/// 亮度🌞 控制条展示
|
||||||
Obx(
|
Obx(
|
||||||
() => Align(
|
() => Align(
|
||||||
alignment: Alignment.center,
|
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
opacity: _ctr.brightnessIndicator.value ? 1.0 : 0.0,
|
opacity: _ctr.brightnessIndicator.value ? 1.0 : 0.0,
|
||||||
@ -384,8 +377,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: <Widget>[
|
||||||
children: [
|
|
||||||
Container(
|
Container(
|
||||||
height: 30.0,
|
height: 30.0,
|
||||||
width: 28.0,
|
width: 28.0,
|
||||||
@ -451,14 +443,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
_.controls = !_.showControls.value;
|
_.controls = !_.showControls.value;
|
||||||
},
|
},
|
||||||
onDoubleTapDown: (details) {
|
onDoubleTapDown: (TapDownDetails details) {
|
||||||
// live模式下禁用 锁定时🔒禁用
|
// live模式下禁用 锁定时🔒禁用
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final totalWidth = MediaQuery.of(context).size.width;
|
final double totalWidth = MediaQuery.sizeOf(context).width;
|
||||||
final tapPosition = details.localPosition.dx;
|
final double tapPosition = details.localPosition.dx;
|
||||||
final sectionWidth = totalWidth / 3;
|
final double sectionWidth = totalWidth / 3;
|
||||||
String type = 'left';
|
String type = 'left';
|
||||||
if (tapPosition < sectionWidth) {
|
if (tapPosition < sectionWidth) {
|
||||||
type = 'left';
|
type = 'left';
|
||||||
@ -469,11 +461,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
doubleTapFuc(type);
|
doubleTapFuc(type);
|
||||||
},
|
},
|
||||||
onLongPressStart: (detail) {
|
onLongPressStart: (LongPressStartDetails detail) {
|
||||||
feedBack();
|
feedBack();
|
||||||
_.setDoubleSpeedStatus(true);
|
_.setDoubleSpeedStatus(true);
|
||||||
},
|
},
|
||||||
onLongPressEnd: (details) {
|
onLongPressEnd: (LongPressEndDetails details) {
|
||||||
_.setDoubleSpeedStatus(false);
|
_.setDoubleSpeedStatus(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -483,16 +475,18 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final tapPosition = details.localPosition.dx;
|
// final double tapPosition = details.localPosition.dx;
|
||||||
int curSliderPosition = _.sliderPosition.value.inMilliseconds;
|
final int curSliderPosition =
|
||||||
double scale = 90000 / MediaQuery.of(context).size.width;
|
_.sliderPosition.value.inMilliseconds;
|
||||||
Duration pos = Duration(
|
final double scale = 90000 / MediaQuery.sizeOf(context).width;
|
||||||
|
final Duration pos = Duration(
|
||||||
milliseconds:
|
milliseconds:
|
||||||
curSliderPosition + (details.delta.dx * scale).round());
|
curSliderPosition + (details.delta.dx * scale).round());
|
||||||
Duration result = pos.clamp(Duration.zero, _.duration.value);
|
final Duration result =
|
||||||
|
pos.clamp(Duration.zero, _.duration.value);
|
||||||
_.onUpdatedSliderProgress(result);
|
_.onUpdatedSliderProgress(result);
|
||||||
_.onChangedSliderStart();
|
_.onChangedSliderStart();
|
||||||
_initTapPositoin = tapPosition;
|
// _initTapPositoin = tapPosition;
|
||||||
},
|
},
|
||||||
onHorizontalDragEnd: (DragEndDetails details) {
|
onHorizontalDragEnd: (DragEndDetails details) {
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
||||||
@ -503,10 +497,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
// 垂直方向 音量/亮度调节
|
// 垂直方向 音量/亮度调节
|
||||||
onVerticalDragUpdate: (DragUpdateDetails details) async {
|
onVerticalDragUpdate: (DragUpdateDetails details) async {
|
||||||
final totalWidth = MediaQuery.of(context).size.width;
|
final double totalWidth = MediaQuery.sizeOf(context).width;
|
||||||
final tapPosition = details.localPosition.dx;
|
final double tapPosition = details.localPosition.dx;
|
||||||
final sectionWidth = totalWidth / 3;
|
final double sectionWidth = totalWidth / 3;
|
||||||
final delta = details.delta.dy;
|
final double delta = details.delta.dy;
|
||||||
|
|
||||||
/// 锁定时禁用
|
/// 锁定时禁用
|
||||||
if (_.controlsLock.value) {
|
if (_.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
@ -518,12 +513,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
if (tapPosition < sectionWidth) {
|
if (tapPosition < sectionWidth) {
|
||||||
// 左边区域 👈
|
// 左边区域 👈
|
||||||
double level = (_.isFullScreen.value
|
final double level = (_.isFullScreen.value
|
||||||
? Get.size.height
|
? Get.size.height
|
||||||
: screenWidth * 9 / 16) *
|
: screenWidth * 9 / 16) *
|
||||||
3;
|
3;
|
||||||
final brightness = _ctr.brightnessValue.value - delta / level;
|
final double brightness =
|
||||||
final result = brightness.clamp(0.0, 1.0);
|
_ctr.brightnessValue.value - delta / level;
|
||||||
|
final double result = brightness.clamp(0.0, 1.0);
|
||||||
setBrightness(result);
|
setBrightness(result);
|
||||||
} else if (tapPosition < sectionWidth * 2) {
|
} else if (tapPosition < sectionWidth * 2) {
|
||||||
// 全屏
|
// 全屏
|
||||||
@ -547,12 +543,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
_distance = dy;
|
_distance = dy;
|
||||||
} else {
|
} else {
|
||||||
// 右边区域 👈
|
// 右边区域 👈
|
||||||
double level = (_.isFullScreen.value
|
final double level = (_.isFullScreen.value
|
||||||
? Get.size.height
|
? Get.size.height
|
||||||
: screenWidth * 9 / 16) *
|
: screenWidth * 9 / 16) *
|
||||||
3;
|
3;
|
||||||
final volume = _ctr.volumeValue.value - delta / level;
|
final double volume = _ctr.volumeValue.value - delta / level;
|
||||||
final result = volume.clamp(0.0, 1.0);
|
final double result = volume.clamp(0.0, 1.0);
|
||||||
setVolume(result);
|
setVolume(result);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -569,7 +565,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
children: [
|
children: [
|
||||||
if (widget.headerControl != null || _.headerControl != null)
|
if (widget.headerControl != null || _.headerControl != null)
|
||||||
ClipRect(
|
ClipRect(
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
child: AppBarAni(
|
child: AppBarAni(
|
||||||
controller: animationController,
|
controller: animationController,
|
||||||
visible: !_.controlsLock.value && _.showControls.value,
|
visible: !_.controlsLock.value && _.showControls.value,
|
||||||
@ -579,7 +574,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
ClipRect(
|
ClipRect(
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
child: AppBarAni(
|
child: AppBarAni(
|
||||||
controller: animationController,
|
controller: animationController,
|
||||||
visible: !_.controlsLock.value && _.showControls.value,
|
visible: !_.controlsLock.value && _.showControls.value,
|
||||||
@ -604,23 +598,23 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
final int buffer = _.bufferedSeconds.value;
|
final int buffer = _.bufferedSeconds.value;
|
||||||
if (defaultBtmProgressBehavior ==
|
if (defaultBtmProgressBehavior ==
|
||||||
BtmProgresBehavior.alwaysHide.code) {
|
BtmProgresBehavior.alwaysHide.code) {
|
||||||
return Container();
|
return nil;
|
||||||
}
|
}
|
||||||
if (defaultBtmProgressBehavior ==
|
if (defaultBtmProgressBehavior ==
|
||||||
BtmProgresBehavior.onlyShowFullScreen.code &&
|
BtmProgresBehavior.onlyShowFullScreen.code &&
|
||||||
!_.isFullScreen.value) {
|
!_.isFullScreen.value) {
|
||||||
return Container();
|
return nil;
|
||||||
} else if (defaultBtmProgressBehavior ==
|
} else if (defaultBtmProgressBehavior ==
|
||||||
BtmProgresBehavior.onlyHideFullScreen.code &&
|
BtmProgresBehavior.onlyHideFullScreen.code &&
|
||||||
_.isFullScreen.value) {
|
_.isFullScreen.value) {
|
||||||
return Container();
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.videoType.value == 'live') {
|
if (_.videoType.value == 'live') {
|
||||||
return Container();
|
return nil;
|
||||||
}
|
}
|
||||||
if (value > max || max <= 0) {
|
if (value > max || max <= 0) {
|
||||||
return Container();
|
return nil;
|
||||||
}
|
}
|
||||||
return Positioned(
|
return Positioned(
|
||||||
bottom: -1.5,
|
bottom: -1.5,
|
||||||
@ -700,7 +694,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
gradient: RadialGradient(
|
gradient: RadialGradient(
|
||||||
center: Alignment.center,
|
|
||||||
colors: [Colors.black26, Colors.transparent],
|
colors: [Colors.black26, Colors.transparent],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -711,7 +704,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Container();
|
return nil;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -732,7 +725,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
_ctr.hideSeekBackwardButton.value ? 0.0 : 1.0,
|
_ctr.hideSeekBackwardButton.value ? 0.0 : 1.0,
|
||||||
),
|
),
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
builder: (context, value, child) => Opacity(
|
builder: (BuildContext context, double value,
|
||||||
|
Widget? child) =>
|
||||||
|
Opacity(
|
||||||
opacity: value,
|
opacity: value,
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
@ -743,14 +738,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: BackwardSeekIndicator(
|
child: BackwardSeekIndicator(
|
||||||
onChanged: (value) {
|
onChanged: (Duration value) {
|
||||||
// _seekBarDeltaValueNotifier.value = -value;
|
// _seekBarDeltaValueNotifier.value = -value;
|
||||||
},
|
},
|
||||||
onSubmitted: (value) {
|
onSubmitted: (Duration value) {
|
||||||
_ctr.hideSeekBackwardButton.value = true;
|
_ctr.hideSeekBackwardButton.value = true;
|
||||||
Player player =
|
final Player player =
|
||||||
widget.controller.videoPlayerController!;
|
widget.controller.videoPlayerController!;
|
||||||
var result = player.state.position - value;
|
Duration result = player.state.position - value;
|
||||||
result = result.clamp(
|
result = result.clamp(
|
||||||
Duration.zero,
|
Duration.zero,
|
||||||
player.state.duration,
|
player.state.duration,
|
||||||
@ -760,11 +755,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: nil,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: MediaQuery.of(context).size.width / 4,
|
width: MediaQuery.sizeOf(context).width / 4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -775,7 +770,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
end: _ctr.hideSeekForwardButton.value ? 0.0 : 1.0,
|
end: _ctr.hideSeekForwardButton.value ? 0.0 : 1.0,
|
||||||
),
|
),
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
builder: (context, value, child) => Opacity(
|
builder: (BuildContext context, double value,
|
||||||
|
Widget? child) =>
|
||||||
|
Opacity(
|
||||||
opacity: value,
|
opacity: value,
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
@ -786,14 +783,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ForwardSeekIndicator(
|
child: ForwardSeekIndicator(
|
||||||
onChanged: (value) {
|
onChanged: (Duration value) {
|
||||||
// _seekBarDeltaValueNotifier.value = value;
|
// _seekBarDeltaValueNotifier.value = value;
|
||||||
},
|
},
|
||||||
onSubmitted: (value) {
|
onSubmitted: (Duration value) {
|
||||||
_ctr.hideSeekForwardButton.value = true;
|
_ctr.hideSeekForwardButton.value = true;
|
||||||
Player player =
|
final Player player =
|
||||||
widget.controller.videoPlayerController!;
|
widget.controller.videoPlayerController!;
|
||||||
var result = player.state.position + value;
|
Duration result = player.state.position + value;
|
||||||
result = result.clamp(
|
result = result.clamp(
|
||||||
Duration.zero,
|
Duration.zero,
|
||||||
player.state.duration,
|
player.state.duration,
|
||||||
@ -803,7 +800,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: nil,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:nil/nil.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/widgets/play_pause_btn.dart';
|
import 'package:pilipala/plugin/pl_player/widgets/play_pause_btn.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
@ -37,7 +38,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final int max = _.durationSeconds.value;
|
final int max = _.durationSeconds.value;
|
||||||
final int buffer = _.bufferedSeconds.value;
|
final int buffer = _.bufferedSeconds.value;
|
||||||
if (value > max || max <= 0) {
|
if (value > max || max <= 0) {
|
||||||
return Container();
|
return nil;
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 7, right: 7, bottom: 6),
|
padding: const EdgeInsets.only(left: 7, right: 7, bottom: 6),
|
||||||
@ -79,8 +80,10 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Obx(() {
|
Obx(() {
|
||||||
return Text(
|
return Text(
|
||||||
_.durationSeconds.value >= 3600
|
_.durationSeconds.value >= 3600
|
||||||
? printDurationWithHours(Duration(seconds: _.positionSeconds.value))
|
? printDurationWithHours(
|
||||||
: printDuration(Duration(seconds: _.positionSeconds.value)),
|
Duration(seconds: _.positionSeconds.value))
|
||||||
|
: printDuration(
|
||||||
|
Duration(seconds: _.positionSeconds.value)),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -90,8 +93,10 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Obx(
|
Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
_.durationSeconds.value >= 3600
|
_.durationSeconds.value >= 3600
|
||||||
? printDurationWithHours(Duration(seconds: _.durationSeconds.value))
|
? printDurationWithHours(
|
||||||
: printDuration(Duration(seconds: _.durationSeconds.value)),
|
Duration(seconds: _.durationSeconds.value))
|
||||||
|
: printDuration(
|
||||||
|
Duration(seconds: _.durationSeconds.value)),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -3,53 +3,54 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/pages/about/index.dart';
|
|
||||||
import 'package:pilipala/pages/blacklist/index.dart';
|
|
||||||
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
|
||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
|
||||||
import 'package:pilipala/pages/fan/index.dart';
|
|
||||||
import 'package:pilipala/pages/fav/index.dart';
|
|
||||||
import 'package:pilipala/pages/favDetail/index.dart';
|
|
||||||
import 'package:pilipala/pages/fav_search/index.dart';
|
|
||||||
import 'package:pilipala/pages/follow/index.dart';
|
|
||||||
import 'package:pilipala/pages/history/index.dart';
|
|
||||||
import 'package:pilipala/pages/history_search/index.dart';
|
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
|
||||||
import 'package:pilipala/pages/hot/index.dart';
|
|
||||||
import 'package:pilipala/pages/html/index.dart';
|
|
||||||
import 'package:pilipala/pages/later/index.dart';
|
|
||||||
import 'package:pilipala/pages/liveRoom/view.dart';
|
|
||||||
import 'package:pilipala/pages/login/index.dart';
|
|
||||||
import 'package:pilipala/pages/member/index.dart';
|
|
||||||
import 'package:pilipala/pages/member_archive/index.dart';
|
|
||||||
import 'package:pilipala/pages/member_coin/index.dart';
|
|
||||||
import 'package:pilipala/pages/member_dynamics/index.dart';
|
|
||||||
import 'package:pilipala/pages/member_like/index.dart';
|
|
||||||
import 'package:pilipala/pages/member_search/index.dart';
|
|
||||||
import 'package:pilipala/pages/member_seasons/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/pages/color_select.dart';
|
|
||||||
import 'package:pilipala/pages/setting/pages/display_mode.dart';
|
|
||||||
import 'package:pilipala/pages/setting/pages/font_size_select.dart';
|
|
||||||
import 'package:pilipala/pages/setting/pages/play_speed_set.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';
|
|
||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
|
||||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
|
||||||
import 'package:pilipala/pages/webview/index.dart';
|
|
||||||
import 'package:pilipala/pages/setting/index.dart';
|
|
||||||
import 'package:pilipala/pages/media/index.dart';
|
|
||||||
import 'package:pilipala/pages/whisper/index.dart';
|
|
||||||
import 'package:pilipala/pages/whisperDetail/index.dart';
|
|
||||||
import 'package:pilipala/utils/storage.dart';
|
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
import '../pages/about/index.dart';
|
||||||
|
import '../pages/blacklist/index.dart';
|
||||||
|
import '../pages/dynamics/detail/index.dart';
|
||||||
|
import '../pages/dynamics/index.dart';
|
||||||
|
import '../pages/fan/index.dart';
|
||||||
|
import '../pages/fav/index.dart';
|
||||||
|
import '../pages/fav_detail/index.dart';
|
||||||
|
import '../pages/fav_search/index.dart';
|
||||||
|
import '../pages/follow/index.dart';
|
||||||
|
import '../pages/history/index.dart';
|
||||||
|
import '../pages/history_search/index.dart';
|
||||||
|
import '../pages/home/index.dart';
|
||||||
|
import '../pages/hot/index.dart';
|
||||||
|
import '../pages/html/index.dart';
|
||||||
|
import '../pages/later/index.dart';
|
||||||
|
import '../pages/live_room/view.dart';
|
||||||
|
import '../pages/login/index.dart';
|
||||||
|
import '../pages/media/index.dart';
|
||||||
|
import '../pages/member/index.dart';
|
||||||
|
import '../pages/member_archive/index.dart';
|
||||||
|
import '../pages/member_coin/index.dart';
|
||||||
|
import '../pages/member_dynamics/index.dart';
|
||||||
|
import '../pages/member_like/index.dart';
|
||||||
|
import '../pages/member_search/index.dart';
|
||||||
|
import '../pages/member_seasons/index.dart';
|
||||||
|
import '../pages/search/index.dart';
|
||||||
|
import '../pages/search_result/index.dart';
|
||||||
|
import '../pages/setting/extra_setting.dart';
|
||||||
|
import '../pages/setting/index.dart';
|
||||||
|
import '../pages/setting/pages/color_select.dart';
|
||||||
|
import '../pages/setting/pages/display_mode.dart';
|
||||||
|
import '../pages/setting/pages/font_size_select.dart';
|
||||||
|
import '../pages/setting/pages/play_speed_set.dart';
|
||||||
|
import '../pages/setting/play_setting.dart';
|
||||||
|
import '../pages/setting/privacy_setting.dart';
|
||||||
|
import '../pages/setting/style_setting.dart';
|
||||||
|
import '../pages/video/detail/index.dart';
|
||||||
|
import '../pages/video/detail/reply_reply/index.dart';
|
||||||
|
import '../pages/webview/index.dart';
|
||||||
|
import '../pages/whisper/index.dart';
|
||||||
|
import '../pages/whisper_detail/index.dart';
|
||||||
|
import '../utils/storage.dart';
|
||||||
|
|
||||||
|
Box<dynamic> setting = GStrorage.setting;
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
static final List<GetPage> getPages = [
|
static final List<GetPage<dynamic>> getPages = [
|
||||||
// 首页(推荐)
|
// 首页(推荐)
|
||||||
CustomGetPage(name: '/', page: () => const HomePage()),
|
CustomGetPage(name: '/', page: () => const HomePage()),
|
||||||
// 热门
|
// 热门
|
||||||
@ -150,22 +151,18 @@ class Routes {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomGetPage extends GetPage {
|
class CustomGetPage extends GetPage<dynamic> {
|
||||||
bool? fullscreen = false;
|
|
||||||
|
|
||||||
CustomGetPage({
|
CustomGetPage({
|
||||||
name,
|
required super.name,
|
||||||
page,
|
required super.page,
|
||||||
this.fullscreen,
|
this.fullscreen,
|
||||||
transitionDuration,
|
super.transitionDuration,
|
||||||
}) : super(
|
}) : super(
|
||||||
name: name,
|
|
||||||
page: page,
|
|
||||||
curve: Curves.linear,
|
curve: Curves.linear,
|
||||||
transition: Transition.native,
|
transition: Transition.native,
|
||||||
showCupertinoParallax: false,
|
showCupertinoParallax: false,
|
||||||
popGesture: false,
|
popGesture: false,
|
||||||
transitionDuration: transitionDuration,
|
|
||||||
fullscreenDialog: fullscreen != null && fullscreen,
|
fullscreenDialog: fullscreen != null && fullscreen,
|
||||||
);
|
);
|
||||||
|
bool? fullscreen = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,15 +2,14 @@ import 'package:appscheme/appscheme.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import '../http/search.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import '../models/common/search_type.dart';
|
||||||
|
|
||||||
import 'id_utils.dart';
|
import 'id_utils.dart';
|
||||||
import 'utils.dart';
|
import 'utils.dart';
|
||||||
|
|
||||||
class PiliSchame {
|
class PiliSchame {
|
||||||
static AppScheme appScheme = AppSchemeImpl.getInstance() as AppScheme;
|
static AppScheme appScheme = AppSchemeImpl.getInstance()!;
|
||||||
static void init() async {
|
static Future<void> init() async {
|
||||||
///
|
///
|
||||||
final SchemeEntity? value = await appScheme.getInitScheme();
|
final SchemeEntity? value = await appScheme.getInitScheme();
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@ -18,14 +17,14 @@ class PiliSchame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 完整链接进入 b23.无效
|
/// 完整链接进入 b23.无效
|
||||||
appScheme.getLatestScheme().then((value) {
|
appScheme.getLatestScheme().then((SchemeEntity? value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
_fullPathPush(value);
|
_fullPathPush(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/// 注册从外部打开的Scheme监听信息 #
|
/// 注册从外部打开的Scheme监听信息 #
|
||||||
appScheme.registerSchemeListener().listen((event) {
|
appScheme.registerSchemeListener().listen((SchemeEntity? event) {
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
_routePush(event);
|
_routePush(event);
|
||||||
}
|
}
|
||||||
@ -41,15 +40,16 @@ class PiliSchame {
|
|||||||
if (scheme == 'bilibili') {
|
if (scheme == 'bilibili') {
|
||||||
// bilibili://root
|
// bilibili://root
|
||||||
if (host == 'root') {
|
if (host == 'root') {
|
||||||
Navigator.popUntil(Get.context!, (route) => route.isFirst);
|
Navigator.popUntil(
|
||||||
|
Get.context!, (Route<dynamic> route) => route.isFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bilibili://space/{uid}
|
// bilibili://space/{uid}
|
||||||
else if (host == 'space') {
|
else if (host == 'space') {
|
||||||
var mid = path.split('/').last;
|
final String mid = path.split('/').last;
|
||||||
Get.toNamed(
|
Get.toNamed<dynamic>(
|
||||||
'/member?mid=$mid',
|
'/member?mid=$mid',
|
||||||
arguments: {'face': null},
|
arguments: <String, dynamic>{'face': null},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,15 +72,15 @@ class PiliSchame {
|
|||||||
|
|
||||||
// bilibili://live/{roomid}
|
// bilibili://live/{roomid}
|
||||||
else if (host == 'live') {
|
else if (host == 'live') {
|
||||||
var roomId = path.split('/').last;
|
final String roomId = path.split('/').last;
|
||||||
Get.toNamed('/liveRoom?roomid=$roomId',
|
Get.toNamed<dynamic>('/liveRoom?roomid=$roomId',
|
||||||
arguments: {'liveItem': null, 'heroTag': roomId.toString()});
|
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId});
|
||||||
}
|
}
|
||||||
|
|
||||||
// bilibili://bangumi/season/${ssid}
|
// bilibili://bangumi/season/${ssid}
|
||||||
else if (host == 'bangumi') {
|
else if (host == 'bangumi') {
|
||||||
if (path.startsWith('/season')) {
|
if (path.startsWith('/season')) {
|
||||||
var seasonId = path.split('/').last;
|
final String seasonId = path.split('/').last;
|
||||||
_bangumiPush(int.parse(seasonId));
|
_bangumiPush(int.parse(seasonId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,8 +104,8 @@ class PiliSchame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 投稿跳转
|
// 投稿跳转
|
||||||
static void _videoPush(int? aidVal, String? bvidVal) async {
|
static Future<void> _videoPush(int? aidVal, String? bvidVal) async {
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
SmartDialog.showLoading<dynamic>(msg: '获取中...');
|
||||||
try {
|
try {
|
||||||
int? aid = aidVal;
|
int? aid = aidVal;
|
||||||
String? bvid = bvidVal;
|
String? bvid = bvidVal;
|
||||||
@ -115,22 +115,24 @@ class PiliSchame {
|
|||||||
if (bvidVal == null) {
|
if (bvidVal == null) {
|
||||||
bvid = IdUtils.av2bv(aidVal!);
|
bvid = IdUtils.av2bv(aidVal!);
|
||||||
}
|
}
|
||||||
int cid = await SearchHttp.ab2c(bvid: bvidVal, aid: aidVal);
|
final int cid = await SearchHttp.ab2c(bvid: bvidVal, aid: aidVal);
|
||||||
String heroTag = Utils.makeHeroTag(aid);
|
final String heroTag = Utils.makeHeroTag(aid);
|
||||||
SmartDialog.dismiss().then(
|
SmartDialog.dismiss<dynamic>().then(
|
||||||
(e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: {
|
// ignore: always_specify_types
|
||||||
|
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
||||||
|
arguments: <String, String?>{
|
||||||
'pic': null,
|
'pic': null,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
SmartDialog.showToast('video获取失败:${e.toString()}');
|
SmartDialog.showToast('video获取失败: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 番剧跳转
|
// 番剧跳转
|
||||||
static void _bangumiPush(int seasonId) async {
|
static Future<void> _bangumiPush(int seasonId) async {
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
SmartDialog.showLoading<dynamic>(msg: '获取中...');
|
||||||
try {
|
try {
|
||||||
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: null);
|
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: null);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
@ -142,7 +144,7 @@ class PiliSchame {
|
|||||||
SmartDialog.dismiss().then(
|
SmartDialog.dismiss().then(
|
||||||
(e) => Get.toNamed(
|
(e) => Get.toNamed(
|
||||||
'/video?bvid=$bvid&cid=$cid&epId=$epId',
|
'/video?bvid=$bvid&cid=$cid&epId=$epId',
|
||||||
arguments: {
|
arguments: <String, dynamic>{
|
||||||
'pic': bangumiDetail.cover,
|
'pic': bangumiDetail.cover,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'videoType': SearchType.media_bangumi,
|
'videoType': SearchType.media_bangumi,
|
||||||
@ -151,14 +153,14 @@ class PiliSchame {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
SmartDialog.showToast('番剧获取失败:${e.toString()}');
|
SmartDialog.showToast('番剧获取失败:$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _fullPathPush(value) async {
|
static Future<void> _fullPathPush(SchemeEntity value) async {
|
||||||
// https://m.bilibili.com/bangumi/play/ss39708
|
// https://m.bilibili.com/bangumi/play/ss39708
|
||||||
// https | m.bilibili.com | /bangumi/play/ss39708
|
// https | m.bilibili.com | /bangumi/play/ss39708
|
||||||
final String scheme = value.scheme!;
|
// final String scheme = value.scheme!;
|
||||||
final String host = value.host!;
|
final String host = value.host!;
|
||||||
final String? path = value.path;
|
final String? path = value.path;
|
||||||
// Map<String, String> query = value.query!;
|
// Map<String, String> query = value.query!;
|
||||||
@ -173,6 +175,7 @@ class PiliSchame {
|
|||||||
print('个人空间');
|
print('个人空间');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
final String area = path.split('/')[1];
|
final String area = path.split('/')[1];
|
||||||
switch (area) {
|
switch (area) {
|
||||||
@ -183,11 +186,11 @@ class PiliSchame {
|
|||||||
break;
|
break;
|
||||||
case 'video':
|
case 'video':
|
||||||
// print('投稿');
|
// print('投稿');
|
||||||
Map map = IdUtils.matchAvorBv(input: path);
|
final Map<String, dynamic> map = IdUtils.matchAvorBv(input: path);
|
||||||
if (map.containsKey('AV')) {
|
if (map.containsKey('AV')) {
|
||||||
_videoPush(map['AV'], null);
|
_videoPush(map['AV']! as int, null);
|
||||||
} else if (map.containsKey('BV')) {
|
} else if (map.containsKey('BV')) {
|
||||||
_videoPush(null, map['BV']);
|
_videoPush(null, map['BV'] as String);
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast('投稿匹配失败');
|
SmartDialog.showToast('投稿匹配失败');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'storage.dart';
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
Box<dynamic> setting = GStrorage.setting;
|
||||||
void feedBack() {
|
void feedBack() {
|
||||||
// 设置中是否开启
|
// 设置中是否开启
|
||||||
bool enable = setting.get(SettingBoxKey.feedBackEnable, defaultValue: false);
|
final bool enable =
|
||||||
|
setting.get(SettingBoxKey.feedBackEnable, defaultValue: false) as bool;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
HapticFeedback.lightImpact();
|
HapticFeedback.lightImpact();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,19 +48,21 @@ class IdUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 匹配
|
// 匹配
|
||||||
static Map matchAvorBv({String? input}) {
|
static Map<String, dynamic> matchAvorBv({String? input}) {
|
||||||
Map result = {};
|
final Map<String, dynamic> result = {'': null};
|
||||||
if (input == null || input == '') {
|
if (input == null || input == '') {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false);
|
final RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false);
|
||||||
RegExp avRegex = RegExp(r'AV\d+', caseSensitive: false);
|
final RegExp avRegex = RegExp(r'AV\d+', caseSensitive: false);
|
||||||
|
|
||||||
Iterable<Match> bvMatches = bvRegex.allMatches(input);
|
final Iterable<Match> bvMatches = bvRegex.allMatches(input);
|
||||||
Iterable<Match> avMatches = avRegex.allMatches(input);
|
final Iterable<Match> avMatches = avRegex.allMatches(input);
|
||||||
|
|
||||||
List<String> bvs = bvMatches.map((match) => match.group(0)!).toList();
|
final List<String> bvs =
|
||||||
List<String> avs = avMatches.map((match) => match.group(0)!).toList();
|
bvMatches.map((Match match) => match.group(0)!).toList();
|
||||||
|
final List<String> avs =
|
||||||
|
avMatches.map((Match match) => match.group(0)!).toList();
|
||||||
|
|
||||||
if (bvs.isNotEmpty) {
|
if (bvs.isNotEmpty) {
|
||||||
result['BV'] = bvs[0].substring(0, 2).toUpperCase() + bvs[0].substring(2);
|
result['BV'] = bvs[0].substring(0, 2).toUpperCase() + bvs[0].substring(2);
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
// import 'package:hive/hive.dart';
|
// import 'package:hive/hive.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:pilipala/models/home/rcmd/result.dart';
|
import 'package:pilipala/models/home/rcmd/result.dart';
|
||||||
@ -7,36 +9,36 @@ import 'package:pilipala/models/search/hot.dart';
|
|||||||
import 'package:pilipala/models/user/info.dart';
|
import 'package:pilipala/models/user/info.dart';
|
||||||
|
|
||||||
class GStrorage {
|
class GStrorage {
|
||||||
static late final Box recVideo;
|
static late final Box<dynamic> recVideo;
|
||||||
static late final Box userInfo;
|
static late final Box<dynamic> userInfo;
|
||||||
static late final Box historyword;
|
static late final Box<dynamic> historyword;
|
||||||
static late final Box localCache;
|
static late final Box<dynamic> localCache;
|
||||||
static late final Box setting;
|
static late final Box<dynamic> setting;
|
||||||
static late final Box video;
|
static late final Box<dynamic> video;
|
||||||
|
|
||||||
static Future<void> init() async {
|
static Future<void> init() async {
|
||||||
final dir = await getApplicationSupportDirectory();
|
final Directory dir = await getApplicationSupportDirectory();
|
||||||
final path = dir.path;
|
final String path = dir.path;
|
||||||
await Hive.initFlutter('$path/hive');
|
await Hive.initFlutter('$path/hive');
|
||||||
regAdapter();
|
regAdapter();
|
||||||
// 首页推荐视频
|
// 首页推荐视频
|
||||||
recVideo = await Hive.openBox(
|
recVideo = await Hive.openBox(
|
||||||
'recVideo',
|
'recVideo',
|
||||||
compactionStrategy: (entries, deletedEntries) {
|
compactionStrategy: (int entries, int deletedEntries) {
|
||||||
return deletedEntries > 12;
|
return deletedEntries > 12;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// 登录用户信息
|
// 登录用户信息
|
||||||
userInfo = await Hive.openBox(
|
userInfo = await Hive.openBox(
|
||||||
'userInfo',
|
'userInfo',
|
||||||
compactionStrategy: (entries, deletedEntries) {
|
compactionStrategy: (int entries, int deletedEntries) {
|
||||||
return deletedEntries > 2;
|
return deletedEntries > 2;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// 本地缓存
|
// 本地缓存
|
||||||
localCache = await Hive.openBox(
|
localCache = await Hive.openBox(
|
||||||
'localCache',
|
'localCache',
|
||||||
compactionStrategy: (entries, deletedEntries) {
|
compactionStrategy: (int entries, int deletedEntries) {
|
||||||
return deletedEntries > 4;
|
return deletedEntries > 4;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -45,13 +47,13 @@ class GStrorage {
|
|||||||
// 搜索历史
|
// 搜索历史
|
||||||
historyword = await Hive.openBox(
|
historyword = await Hive.openBox(
|
||||||
'historyWord',
|
'historyWord',
|
||||||
compactionStrategy: (entries, deletedEntries) {
|
compactionStrategy: (int entries, int deletedEntries) {
|
||||||
return deletedEntries > 10;
|
return deletedEntries > 10;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static regAdapter() {
|
static void regAdapter() {
|
||||||
Hive.registerAdapter(RecVideoItemAppModelAdapter());
|
Hive.registerAdapter(RecVideoItemAppModelAdapter());
|
||||||
Hive.registerAdapter(RcmdReasonAdapter());
|
Hive.registerAdapter(RcmdReasonAdapter());
|
||||||
Hive.registerAdapter(RcmdStatAdapter());
|
Hive.registerAdapter(RcmdStatAdapter());
|
||||||
@ -88,7 +90,7 @@ class GStrorage {
|
|||||||
|
|
||||||
class SettingBoxKey {
|
class SettingBoxKey {
|
||||||
/// 播放器
|
/// 播放器
|
||||||
static const btmProgressBehavior = 'btmProgressBehavior',
|
static const String btmProgressBehavior = 'btmProgressBehavior',
|
||||||
defaultVideoSpeed = 'defaultVideoSpeed',
|
defaultVideoSpeed = 'defaultVideoSpeed',
|
||||||
autoUpgradeEnable = 'autoUpgradeEnable',
|
autoUpgradeEnable = 'autoUpgradeEnable',
|
||||||
feedBackEnable = 'feedBackEnable',
|
feedBackEnable = 'feedBackEnable',
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user