Merge branch 'design'

This commit is contained in:
guozhigq
2024-01-07 21:06:48 +08:00
104 changed files with 1077 additions and 1032 deletions

View File

@ -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),

View File

@ -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)),
}, },
); );
} }

View File

@ -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(

View File

@ -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,

View File

@ -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),

View File

@ -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(

View File

@ -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,

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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,21 +21,23 @@ 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代码弹幕不能使用 9BAS弹幕pool必须为2) int mode =
1, // 弹幕类型(1滚动弹幕 4底端弹幕 5顶端弹幕 6逆向弹幕(不能使用) 7高级弹幕 8代码弹幕不能使用 9BAS弹幕pool必须为2)
// String? aid,// 稿件avid // String? aid,// 稿件avid
// String? bvid,// bvid与aid必须有一个 // String? bvid,// bvid与aid必须有一个
required String bvid, required String bvid,
int? progress,// 弹幕出现在视频内的时间单位为毫秒默认为0 int? progress, // 弹幕出现在视频内的时间单位为毫秒默认为0
int? color,// 弹幕颜色(默认白色16777215 int? color, // 弹幕颜色(默认白色16777215
int? fontsize,// 弹幕字号默认25 int? fontsize, // 弹幕字号默认25
int? pool,// 弹幕池选择0普通池 1字幕池 2特殊池代码/BAS弹幕默认普通池0 int? pool, // 弹幕池选择0普通池 1字幕池 2特殊池代码/BAS弹幕默认普通池0
//int? rnd,// 当前时间戳*1000000若无此项则发送弹幕冷却时间限制为90s若有此项则发送弹幕冷却时间限制为5s //int? rnd,// 当前时间戳*1000000若无此项则发送弹幕冷却时间限制为90s若有此项则发送弹幕冷却时间限制为5s
int? colorful,//60001专属渐变彩色需要会员 int? colorful, //60001专属渐变彩色需要会员
int? checkbox_type,//是否带 UP 身份标识0普通4带有标识 int? checkbox_type, //是否带 UP 身份标识0普通4带有标识
// String? csrf,//CSRF Token位于 Cookie Cookie 方式必要 // String? csrf,//CSRF Token位于 Cookie Cookie 方式必要
// String? access_key,// APP 登录 Token APP 方式必要 // String? access_key,// APP 登录 Token APP 方式必要
}) async { }) async {

View File

@ -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({

View File

@ -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 {

View File

@ -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(

View File

@ -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

View File

@ -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;

View File

@ -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 '';
} }
} }
} }

View File

@ -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(

View File

@ -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 {

View File

@ -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({

View File

@ -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 {
// 会话列表 // 会话列表

View File

@ -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({

View File

@ -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,

View File

@ -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 {

View File

@ -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'] 为结果

View File

@ -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),
), ),

View File

@ -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;

View File

@ -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
setState(() => isProcessing = true); ? null
await action(); : () async {
setState(() => isProcessing = false); setState(() => isProcessing = true);
}; await action();
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

View File

@ -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,
), ),

View File

@ -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);
@ -212,78 +213,74 @@ class _BangumiPanelState extends State<BangumiPanel> {
SizedBox( SizedBox(
height: 60, height: 60,
child: ListView.builder( child: ListView.builder(
controller: listViewScrollCtr, controller: listViewScrollCtr,
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),
child: Material( child: Material(
color: Theme.of(context).colorScheme.onInverseSurface, color: Theme.of(context).colorScheme.onInverseSurface,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
child: InkWell( child: InkWell(
onTap: () => changeFucCall(widget.pages[i], i), onTap: () => changeFucCall(widget.pages[i], i),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
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)
],
Text(
'${i + 1}',
style: TextStyle(
fontSize: 13,
color: i == currentIndex
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface),
), ),
const SizedBox(width: 2), const SizedBox(width: 6)
if (widget.pages[i].badge != null) ...[
Image.asset(
'assets/images/big-vip.png',
height: 16,
),
],
], ],
), Text(
const SizedBox(height: 3), '${i + 1}',
Text( style: TextStyle(
widget.pages[i].longTitle!, fontSize: 13,
maxLines: 1, color: i == currentIndex
style: TextStyle( ? Theme.of(context).colorScheme.primary
fontSize: 13, : Theme.of(context)
color: i == currentIndex .colorScheme
? Theme.of(context).colorScheme.primary .onSurface),
: Theme.of(context) ),
.colorScheme const SizedBox(width: 2),
.onSurface), if (widget.pages[i].badge != null) ...[
overflow: TextOverflow.ellipsis, Image.asset(
) 'assets/images/big-vip.png',
], height: 16,
), ),
],
],
),
const SizedBox(height: 3),
Text(
widget.pages[i].longTitle!,
maxLines: 1,
style: TextStyle(
fontSize: 13,
color: i == currentIndex
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface),
overflow: TextOverflow.ellipsis,
)
],
), ),
), ),
), ),
); ),
})), );
},
),
) )
], ],
); );

View File

@ -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(

View File

@ -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']) {

View File

@ -1,26 +1,23 @@
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);
final int cid; final int cid;
Map<int,List<DanmakuElem>> dmSegMap = {}; Map<int, List<DanmakuElem>> dmSegMap = {};
// 已请求的段落标记 // 已请求的段落标记
List<bool> requestedSeg = []; List<bool> requestedSeg = [];
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,17 +26,17 @@ 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秒存储一次
if (dmSegMap[pos] == null) { if (dmSegMap[pos] == null) {
dmSegMap[pos] = []; dmSegMap[pos] = [];
} }

View File

@ -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;
@ -87,7 +83,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
return; return;
} }
int currentPosition = position.inMilliseconds; int currentPosition = position.inMilliseconds;
currentPosition -= currentPosition % 100;//取整百的毫秒数 currentPosition -= currentPosition % 100; //取整百的毫秒数
if (currentPosition == latestAddedPosition) { if (currentPosition == latestAddedPosition) {
return; return;
@ -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),
), ),

View File

@ -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';

View File

@ -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';

View File

@ -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) {

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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(

View File

@ -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,

View File

@ -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,

View File

@ -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) {

View File

@ -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({

View File

@ -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 {

View File

@ -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)) {

View File

@ -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!,

View File

@ -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
setState(() => isProcessing = true); ? null
await action(); : () async {
setState(() => isProcessing = false); setState(() => isProcessing = true);
}; await action();
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),

View File

@ -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(

View File

@ -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']) {

View File

@ -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),

View File

@ -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(

View File

@ -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> {
), ),
), ),
); );
}), },
), ),
) )
], ],

View File

@ -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!}',

View File

@ -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,

View File

@ -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);
} }

View File

@ -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) {

View File

@ -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);
}, },
); );

View File

@ -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,19 +47,23 @@ 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
setState(() => isProcessing = true); ? null
await action(); : () async {
setState(() => isProcessing = false); setState(() => isProcessing = true);
}; await action();
setState(() => isProcessing = false);
};
} }
@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(
key: ValueKey<int>(widget.replyItem!.like!), widget.replyItem!.like.toString(),
style: TextStyle( key: ValueKey<int>(widget.replyItem!.like!),
color: widget.replyItem!.action == 1 ? primary : color, style: TextStyle(
fontSize: color: widget.replyItem!.action == 1 ? primary : color,
Theme.of(context).textTheme.labelSmall!.fontSize)), fontSize: t.textTheme.labelSmall!.fontSize,
),
),
), ),
], ],
), ),

View File

@ -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) {

View File

@ -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),
); );

View File

@ -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(

View File

@ -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,

View File

@ -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,14 +787,16 @@ 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(
val/widget.controller!.playbackSpeed); duration:
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,17 +890,20 @@ 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>{
SystemChrome.setPreferredOrientations([ if (MediaQuery.of(context).orientation ==
DeviceOrientation.portraitUp, Orientation.landscape)
]) {
}, SystemChrome.setPreferredOrientations([
Get.back() DeviceOrientation.portraitUp,
} ])
},
Get.back()
}
}, },
), ),
SizedBox(width: buttonSpace), SizedBox(width: buttonSpace),
@ -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,
), ),
), ),

View File

@ -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()),

View File

@ -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(

View File

@ -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 =

View File

@ -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,9 +941,10 @@ 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 ||
(mode == FullScreenMode.auto && direction.value == 'vertical')) { (mode == FullScreenMode.auto && direction.value == 'vertical')) {
await verticalScreen(); await verticalScreen();
} else { } else {

View File

@ -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];
} }

View File

@ -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];
} }

View File

@ -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,
@ -633,7 +627,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
progressBarColor: colorTheme, progressBarColor: colorTheme,
baseBarColor: Colors.white.withOpacity(0.2), baseBarColor: Colors.white.withOpacity(0.2),
bufferedBarColor: bufferedBarColor:
Theme.of(context).colorScheme.primary.withOpacity(0.4), Theme.of(context).colorScheme.primary.withOpacity(0.4),
timeLabelLocation: TimeLabelLocation.none, timeLabelLocation: TimeLabelLocation.none,
thumbColor: colorTheme, thumbColor: colorTheme,
barHeight: 3, barHeight: 3,
@ -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,
), ),
], ],
), ),

View File

@ -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,
), ),
), ),

View File

@ -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;
} }

View File

@ -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
'pic': null, (e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
'heroTag': heroTag, arguments: <String, String?>{
}), 'pic': null,
'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('投稿匹配失败');
} }

View File

@ -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();
} }

View File

@ -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);

View File

@ -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