Merge branch 'design' into alpha
This commit is contained in:
@ -306,4 +306,14 @@ class Api {
|
|||||||
static const String onlineTotal = '/x/player/online/total';
|
static const String onlineTotal = '/x/player/online/total';
|
||||||
|
|
||||||
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
||||||
|
|
||||||
|
// up主分组
|
||||||
|
static const String followUpTag = '/x/relation/tags';
|
||||||
|
|
||||||
|
// 设置Up主分组
|
||||||
|
// 0 添加至默认分组 否则使用,分割tagid
|
||||||
|
static const String addUsers = '/x/relation/tags/addUsers';
|
||||||
|
|
||||||
|
// 获取指定分组下的up
|
||||||
|
static const String followUpGroup = '/x/relation/tag';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import 'package:pilipala/http/index.dart';
|
import 'package:pilipala/http/index.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
|
import 'package:pilipala/models/follow/result.dart';
|
||||||
import 'package:pilipala/models/member/archive.dart';
|
import 'package:pilipala/models/member/archive.dart';
|
||||||
import 'package:pilipala/models/member/info.dart';
|
import 'package:pilipala/models/member/info.dart';
|
||||||
|
import 'package:pilipala/models/member/tags.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
import 'package:pilipala/utils/wbi_sign.dart';
|
||||||
|
|
||||||
class MemberHttp {
|
class MemberHttp {
|
||||||
@ -144,4 +146,73 @@ class MemberHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询分组
|
||||||
|
static Future followUpTags() async {
|
||||||
|
var res = await Request().get(Api.followUpTag);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data']
|
||||||
|
.map<MemberTagItemModel>((e) => MemberTagItemModel.fromJson(e))
|
||||||
|
.toList()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置分组
|
||||||
|
static Future addUsers(int? fids, String? tagids) async {
|
||||||
|
var res = await Request().post(Api.addUsers, queryParameters: {
|
||||||
|
'fids': fids,
|
||||||
|
'tagids': tagids ?? '0',
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
}, data: {
|
||||||
|
'cross_domain': true
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': [], 'msg': '操作成功'};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取某分组下的up
|
||||||
|
static Future followUpGroup(
|
||||||
|
int? mid,
|
||||||
|
int? tagid,
|
||||||
|
int? pn,
|
||||||
|
int? ps,
|
||||||
|
) async {
|
||||||
|
var res = await Request().get(Api.followUpGroup, data: {
|
||||||
|
'mid': mid,
|
||||||
|
'tagid': tagid,
|
||||||
|
'pn': pn,
|
||||||
|
'ps': ps,
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
// FollowItemModel
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data']
|
||||||
|
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
|
||||||
|
.toList()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
import 'package:pilipala/http/index.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import 'package:pilipala/models/bangumi/info.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
import 'package:pilipala/models/search/hot.dart';
|
import 'package:pilipala/models/search/hot.dart';
|
||||||
import 'package:pilipala/models/search/result.dart';
|
import 'package:pilipala/models/search/result.dart';
|
||||||
import 'package:pilipala/models/search/suggest.dart';
|
import 'package:pilipala/models/search/suggest.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class SearchHttp {
|
class SearchHttp {
|
||||||
|
static Box setting = GStrorage.setting;
|
||||||
static Future hotSearchList() async {
|
static Future hotSearchList() async {
|
||||||
var res = await Request().get(Api.hotSearchList);
|
var res = await Request().get(Api.hotSearchList);
|
||||||
if (res.data is String) {
|
if (res.data is String) {
|
||||||
@ -78,6 +81,12 @@ class SearchHttp {
|
|||||||
try {
|
try {
|
||||||
switch (searchType) {
|
switch (searchType) {
|
||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
|
List<int> blackMidsList =
|
||||||
|
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
|
||||||
|
for (var i in res.data['data']['result']) {
|
||||||
|
// 屏蔽推广和拉黑用户
|
||||||
|
i['available'] = !blackMidsList.contains(i['mid']);
|
||||||
|
}
|
||||||
data = SearchVideoModel.fromJson(res.data['data']);
|
data = SearchVideoModel.fromJson(res.data['data']);
|
||||||
break;
|
break;
|
||||||
case SearchType.live_room:
|
case SearchType.live_room:
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class FollowDataModel {
|
|||||||
List<FollowItemModel>? list;
|
List<FollowItemModel>? list;
|
||||||
|
|
||||||
FollowDataModel.fromJson(Map<String, dynamic> json) {
|
FollowDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
total = json['total'];
|
total = json['total'] ?? 0;
|
||||||
list = json['list']
|
list = json['list']
|
||||||
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
|
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
@ -19,7 +19,7 @@ class FollowItemModel {
|
|||||||
FollowItemModel({
|
FollowItemModel({
|
||||||
this.mid,
|
this.mid,
|
||||||
this.attribute,
|
this.attribute,
|
||||||
this.mtime,
|
// this.mtime,
|
||||||
this.tag,
|
this.tag,
|
||||||
this.special,
|
this.special,
|
||||||
this.uname,
|
this.uname,
|
||||||
@ -30,7 +30,7 @@ class FollowItemModel {
|
|||||||
|
|
||||||
int? mid;
|
int? mid;
|
||||||
int? attribute;
|
int? attribute;
|
||||||
int? mtime;
|
// int? mtime;
|
||||||
List? tag;
|
List? tag;
|
||||||
int? special;
|
int? special;
|
||||||
String? uname;
|
String? uname;
|
||||||
@ -41,7 +41,7 @@ class FollowItemModel {
|
|||||||
FollowItemModel.fromJson(Map<String, dynamic> json) {
|
FollowItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
mid = json['mid'];
|
mid = json['mid'];
|
||||||
attribute = json['attribute'];
|
attribute = json['attribute'];
|
||||||
mtime = json['mtime'];
|
// mtime = json['mtime'];
|
||||||
tag = json['tag'];
|
tag = json['tag'];
|
||||||
special = json['special'];
|
special = json['special'];
|
||||||
uname = json['uname'];
|
uname = json['uname'];
|
||||||
|
|||||||
23
lib/models/member/tags.dart
Normal file
23
lib/models/member/tags.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
class MemberTagItemModel {
|
||||||
|
MemberTagItemModel({
|
||||||
|
this.count,
|
||||||
|
this.name,
|
||||||
|
this.tagid,
|
||||||
|
this.tip,
|
||||||
|
this.checked,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? count;
|
||||||
|
String? name;
|
||||||
|
int? tagid;
|
||||||
|
String? tip;
|
||||||
|
bool? checked;
|
||||||
|
|
||||||
|
MemberTagItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
count = json['count'];
|
||||||
|
name = json['name'];
|
||||||
|
tagid = json['tagid'];
|
||||||
|
tip = json['tip'];
|
||||||
|
checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ class SearchVideoModel {
|
|||||||
List<SearchVideoItemModel>? list;
|
List<SearchVideoItemModel>? list;
|
||||||
SearchVideoModel.fromJson(Map<String, dynamic> json) {
|
SearchVideoModel.fromJson(Map<String, dynamic> json) {
|
||||||
list = json['result']
|
list = json['result']
|
||||||
|
.where((e) => e['available'] == true)
|
||||||
.map<SearchVideoItemModel>((e) => SearchVideoItemModel.fromJson(e))
|
.map<SearchVideoItemModel>((e) => SearchVideoItemModel.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
@ -17,7 +18,7 @@ class SearchVideoItemModel {
|
|||||||
this.id,
|
this.id,
|
||||||
this.cid,
|
this.cid,
|
||||||
// this.author,
|
// this.author,
|
||||||
// this.mid,
|
this.mid,
|
||||||
// this.typeid,
|
// this.typeid,
|
||||||
// this.typename,
|
// this.typename,
|
||||||
this.arcurl,
|
this.arcurl,
|
||||||
@ -47,7 +48,7 @@ class SearchVideoItemModel {
|
|||||||
int? id;
|
int? id;
|
||||||
int? cid;
|
int? cid;
|
||||||
// String? author;
|
// String? author;
|
||||||
// String? mid;
|
int? mid;
|
||||||
// String? typeid;
|
// String? typeid;
|
||||||
// String? typename;
|
// String? typename;
|
||||||
String? arcurl;
|
String? arcurl;
|
||||||
@ -80,6 +81,7 @@ class SearchVideoItemModel {
|
|||||||
arcurl = json['arcurl'];
|
arcurl = json['arcurl'];
|
||||||
aid = json['aid'];
|
aid = json['aid'];
|
||||||
bvid = json['bvid'];
|
bvid = json['bvid'];
|
||||||
|
mid = json['mid'];
|
||||||
// title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
|
// title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
|
||||||
title = Em.regTitle(json['title']);
|
title = Em.regTitle(json['title']);
|
||||||
description = json['description'];
|
description = json['description'];
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:pilipala/models/bangumi/info.dart';
|
|||||||
import 'package:pilipala/models/user/fav_folder.dart';
|
import 'package:pilipala/models/user/fav_folder.dart';
|
||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.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';
|
||||||
@ -21,7 +22,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
? int.parse(Get.parameters['seasonId']!)
|
? int.parse(Get.parameters['seasonId']!)
|
||||||
: null;
|
: null;
|
||||||
var epId = Get.parameters['epId'] != null
|
var epId = Get.parameters['epId'] != null
|
||||||
? int.parse(Get.parameters['epId']!)
|
? int.tryParse(Get.parameters['epId']!)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// 是否预渲染 骨架屏
|
// 是否预渲染 骨架屏
|
||||||
@ -257,7 +258,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
VideoDetailController videoDetailCtr =
|
VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
videoDetailCtr.bvid = bvid;
|
videoDetailCtr.bvid = bvid;
|
||||||
videoDetailCtr.cid = cid;
|
videoDetailCtr.cid.value = cid;
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
@ -292,4 +293,31 @@ class BangumiIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 列表循环或者顺序播放时,自动播放下一个
|
||||||
|
void nextPlay() {
|
||||||
|
late List episodes;
|
||||||
|
if (bangumiDetail.value.episodes != null) {
|
||||||
|
episodes = bangumiDetail.value.episodes!;
|
||||||
|
}
|
||||||
|
VideoDetailController videoDetailCtr =
|
||||||
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
|
int currentIndex =
|
||||||
|
episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value);
|
||||||
|
int nextIndex = currentIndex + 1;
|
||||||
|
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
|
// 列表循环
|
||||||
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
|
if (nextIndex == episodes.length - 1) {
|
||||||
|
nextIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextIndex <= episodes.length - 1 &&
|
||||||
|
platRepeat == PlayRepeat.listOrder) {}
|
||||||
|
|
||||||
|
int cid = episodes[nextIndex].cid!;
|
||||||
|
String bvid = episodes[nextIndex].bvid!;
|
||||||
|
int aid = episodes[nextIndex].aid!;
|
||||||
|
changeSeasonOrbangu(bvid, cid, aid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,10 +34,12 @@ class BangumiIntroPanel extends StatefulWidget {
|
|||||||
|
|
||||||
class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
final BangumiIntroController bangumiIntroController =
|
late BangumiIntroController bangumiIntroController;
|
||||||
Get.put(BangumiIntroController(), tag: Get.arguments['heroTag']);
|
late VideoDetailController videoDetailCtr;
|
||||||
BangumiInfoModel? bangumiDetail;
|
BangumiInfoModel? bangumiDetail;
|
||||||
late Future _futureBuilderFuture;
|
late Future _futureBuilderFuture;
|
||||||
|
late int cid;
|
||||||
|
late String heroTag;
|
||||||
|
|
||||||
// 添加页面缓存
|
// 添加页面缓存
|
||||||
@override
|
@override
|
||||||
@ -46,10 +48,19 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
heroTag = Get.arguments['heroTag'];
|
||||||
|
cid = widget.cid!;
|
||||||
|
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
|
||||||
|
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
bangumiIntroController.bangumiDetail.listen((value) {
|
bangumiIntroController.bangumiDetail.listen((value) {
|
||||||
bangumiDetail = value;
|
bangumiDetail = value;
|
||||||
});
|
});
|
||||||
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
|
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
|
||||||
|
videoDetailCtr.cid.listen((p0) {
|
||||||
|
print('🐶🐶$p0');
|
||||||
|
cid = p0;
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -61,9 +72,11 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data['status']) {
|
if (snapshot.data['status']) {
|
||||||
// 请求成功
|
// 请求成功
|
||||||
|
|
||||||
return BangumiInfo(
|
return BangumiInfo(
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
bangumiDetail: bangumiDetail,
|
bangumiDetail: bangumiDetail,
|
||||||
|
cid: cid,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 请求错误
|
// 请求错误
|
||||||
@ -77,7 +90,7 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
return BangumiInfo(
|
return BangumiInfo(
|
||||||
loadingStatus: true,
|
loadingStatus: true,
|
||||||
bangumiDetail: bangumiDetail,
|
bangumiDetail: bangumiDetail,
|
||||||
cid: widget.cid,
|
cid: cid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -118,6 +131,12 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
bangumiItem = bangumiIntroController.bangumiItem;
|
bangumiItem = bangumiIntroController.bangumiItem;
|
||||||
sheetHeight = localCache.get('sheetHeight');
|
sheetHeight = localCache.get('sheetHeight');
|
||||||
cid = widget.cid!;
|
cid = widget.cid!;
|
||||||
|
print('cid: $cid');
|
||||||
|
videoDetailCtr.cid.listen((p0) {
|
||||||
|
cid = p0;
|
||||||
|
print('cid: $cid');
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收藏
|
// 收藏
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
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:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import 'package:pilipala/models/bangumi/info.dart';
|
||||||
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class BangumiPanel extends StatefulWidget {
|
class BangumiPanel extends StatefulWidget {
|
||||||
@ -30,16 +32,28 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
dynamic userInfo;
|
dynamic userInfo;
|
||||||
// 默认未开通
|
// 默认未开通
|
||||||
int vipStatus = 0;
|
int vipStatus = 0;
|
||||||
|
late int cid;
|
||||||
|
String heroTag = Get.arguments['heroTag'];
|
||||||
|
late final VideoDetailController videoDetailCtr;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
currentIndex = widget.pages.indexWhere((e) => e.cid == widget.cid!);
|
cid = widget.cid!;
|
||||||
|
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
vipStatus = userInfo.vipStatus;
|
vipStatus = userInfo.vipStatus;
|
||||||
}
|
}
|
||||||
|
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
|
||||||
|
videoDetailCtr.cid.listen((p0) {
|
||||||
|
cid = p0;
|
||||||
|
setState(() {});
|
||||||
|
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
|
||||||
|
scrollToIndex();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -177,7 +177,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
return AnimatedOpacity(
|
return AnimatedOpacity(
|
||||||
opacity: snapshot.data ? 1 : 0,
|
opacity: snapshot.data ? 1 : 0,
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: author(_dynamicDetailController!.item, context),
|
child: AuthorPanel(item: _dynamicDetailController.item),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
Widget author(item, context) {
|
class AuthorPanel extends StatelessWidget {
|
||||||
|
final dynamic item;
|
||||||
|
const AuthorPanel({super.key, required this.item});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(item.modules.moduleAuthor.mid);
|
String heroTag = Utils.makeHeroTag(item.modules.moduleAuthor.mid);
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
@ -60,6 +67,93 @@ Widget author(item, context) {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const Spacer(),
|
||||||
|
if (item.type == 'DYNAMIC_TYPE_AV')
|
||||||
|
SizedBox(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
child: IconButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) {
|
||||||
|
return MorePanel(item: item);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MorePanel extends StatelessWidget {
|
||||||
|
final dynamic item;
|
||||||
|
const MorePanel({super.key, required this.item});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||||
|
// clipBehavior: Clip.hardEdge,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () => Get.back(),
|
||||||
|
child: Container(
|
||||||
|
height: 35,
|
||||||
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 32,
|
||||||
|
height: 3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () async {
|
||||||
|
try {
|
||||||
|
String bvid = item.modules.moduleDynamic.major.archive.bvid;
|
||||||
|
var res = await UserHttp.toViewLater(bvid: bvid);
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
Get.back();
|
||||||
|
} catch (err) {
|
||||||
|
SmartDialog.showToast('出错了:${err.toString()}');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
// dense: true,
|
||||||
|
leading: const Icon(Icons.watch_later_outlined, size: 19),
|
||||||
|
title: Text(
|
||||||
|
'稍后再看',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(thickness: 0.1, height: 1),
|
||||||
|
ListTile(
|
||||||
|
onTap: () => Get.back(),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
dense: true,
|
||||||
|
title: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ class DynamicPanel extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||||
child: author(item, context),
|
child: AuthorPanel(item: item),
|
||||||
),
|
),
|
||||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||||
item!.modules!.moduleDynamic!.major != null)
|
item!.modules!.moduleDynamic!.major != null)
|
||||||
|
|||||||
@ -1,20 +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:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/follow.dart';
|
import 'package:pilipala/http/follow.dart';
|
||||||
|
import 'package:pilipala/http/member.dart';
|
||||||
import 'package:pilipala/models/follow/result.dart';
|
import 'package:pilipala/models/follow/result.dart';
|
||||||
|
import 'package:pilipala/models/member/tags.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class FollowController extends GetxController {
|
/// 查看自己的关注时,可以查看分类
|
||||||
|
/// 查看其他人的关注时,只可以看全部
|
||||||
|
class FollowController extends GetxController with GetTickerProviderStateMixin {
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
int pn = 1;
|
int pn = 1;
|
||||||
int ps = 20;
|
int ps = 20;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
RxList<FollowItemModel> followList = [FollowItemModel()].obs;
|
RxList<FollowItemModel> followList = <FollowItemModel>[].obs;
|
||||||
late int mid;
|
late int mid;
|
||||||
late String name;
|
late String name;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
RxString loadingText = '加载中...'.obs;
|
RxString loadingText = '加载中...'.obs;
|
||||||
|
RxBool isOwner = false.obs;
|
||||||
|
late List<MemberTagItemModel> followTags;
|
||||||
|
late TabController tabController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -23,6 +31,7 @@ class FollowController extends GetxController {
|
|||||||
mid = Get.parameters['mid'] != null
|
mid = Get.parameters['mid'] != null
|
||||||
? int.parse(Get.parameters['mid']!)
|
? int.parse(Get.parameters['mid']!)
|
||||||
: userInfo.mid;
|
: userInfo.mid;
|
||||||
|
isOwner.value = mid == userInfo.mid;
|
||||||
name = Get.parameters['name'] ?? userInfo.uname;
|
name = Get.parameters['name'] ?? userInfo.uname;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,4 +65,20 @@ class FollowController extends GetxController {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当查看当前用户的关注时,请求关注分组
|
||||||
|
Future followUpTags() async {
|
||||||
|
if (userInfo != null && mid == userInfo.mid) {
|
||||||
|
var res = await MemberHttp.followUpTags();
|
||||||
|
if (res['status']) {
|
||||||
|
followTags = res['data'];
|
||||||
|
tabController = TabController(
|
||||||
|
initialIndex: 0,
|
||||||
|
length: res['data'].length,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,8 @@
|
|||||||
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:pilipala/common/widgets/http_error.dart';
|
|
||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
|
||||||
import 'package:pilipala/models/follow/result.dart';
|
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/follow_item.dart';
|
import 'widgets/follow_list.dart';
|
||||||
|
import 'widgets/owner_follow_list.dart';
|
||||||
|
|
||||||
class FollowPage extends StatefulWidget {
|
class FollowPage extends StatefulWidget {
|
||||||
const FollowPage({super.key});
|
const FollowPage({super.key});
|
||||||
@ -19,30 +15,12 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
late String mid;
|
late String mid;
|
||||||
late FollowController _followController;
|
late FollowController _followController;
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
Future? _futureBuilderFuture;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
mid = Get.parameters['mid']!;
|
mid = Get.parameters['mid']!;
|
||||||
_followController = Get.put(FollowController(), tag: mid);
|
_followController = Get.put(FollowController(), tag: mid);
|
||||||
_futureBuilderFuture = _followController.queryFollowings('init');
|
|
||||||
scrollController.addListener(
|
|
||||||
() async {
|
|
||||||
if (scrollController.position.pixels >=
|
|
||||||
scrollController.position.maxScrollExtent - 200) {
|
|
||||||
EasyThrottle.throttle('follow', const Duration(seconds: 1), () {
|
|
||||||
_followController.queryFollowings('onLoad');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
scrollController.removeListener(() {});
|
|
||||||
super.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -54,73 +32,57 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: Text(
|
title: Text(
|
||||||
'${_followController.name}的关注',
|
_followController.isOwner.value
|
||||||
|
? '我的关注'
|
||||||
|
: '${_followController.name}的关注',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: Obx(
|
||||||
onRefresh: () async =>
|
() => !_followController.isOwner.value
|
||||||
await _followController.queryFollowings('init'),
|
? FollowList(ctr: _followController)
|
||||||
child: FutureBuilder(
|
: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _followController.followUpTags(),
|
||||||
builder: (context, snapshot) {
|
builder: (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']) {
|
||||||
List<FollowItemModel> list = _followController.followList;
|
return Column(
|
||||||
return Obx(
|
children: [
|
||||||
() => list.isNotEmpty
|
TabBar(
|
||||||
? ListView.builder(
|
controller: _followController.tabController,
|
||||||
controller: scrollController,
|
isScrollable: true,
|
||||||
itemCount: list.length + 1,
|
tabs: [
|
||||||
itemBuilder: (BuildContext context, int index) {
|
for (var i in data['data']) ...[
|
||||||
if (index == list.length) {
|
Tab(text: i.name),
|
||||||
return Container(
|
]
|
||||||
height:
|
]),
|
||||||
MediaQuery.of(context).padding.bottom +
|
Expanded(
|
||||||
60,
|
child: TabBarView(
|
||||||
padding: EdgeInsets.only(
|
controller: _followController.tabController,
|
||||||
bottom: MediaQuery.of(context)
|
children: [
|
||||||
.padding
|
for (var i = 0;
|
||||||
.bottom),
|
i < _followController.tabController.length;
|
||||||
child: Center(
|
i++) ...[
|
||||||
child: Obx(
|
OwnerFollowList(
|
||||||
() => Text(
|
ctr: _followController,
|
||||||
_followController.loadingText.value,
|
tagItem: _followController.followTags[i],
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline,
|
|
||||||
fontSize: 13),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return followItem(item: list[index]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
: const CustomScrollView(
|
]
|
||||||
slivers: [NoData()],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
HttpError(
|
|
||||||
errMsg: data['msg'],
|
|
||||||
fn: () => _followController.queryFollowings('init'),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,16 @@
|
|||||||
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/models/follow/result.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
Widget followItem({item}) {
|
class FollowItem extends StatelessWidget {
|
||||||
|
final FollowItemModel item;
|
||||||
|
const FollowItem({super.key, required this.item});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(item!.mid);
|
String heroTag = Utils.makeHeroTag(item!.mid);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -22,13 +28,13 @@ Widget followItem({item}) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
item.uname,
|
item.uname!,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontSize: 14),
|
style: const TextStyle(fontSize: 14),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
item.sign,
|
item.sign!,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@ -36,3 +42,4 @@ Widget followItem({item}) {
|
|||||||
trailing: const SizedBox(width: 6),
|
trailing: const SizedBox(width: 6),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
111
lib/pages/follow/widgets/follow_list.dart
Normal file
111
lib/pages/follow/widgets/follow_list.dart
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
|
import 'package:pilipala/models/follow/result.dart';
|
||||||
|
import 'package:pilipala/pages/follow/index.dart';
|
||||||
|
|
||||||
|
import 'follow_item.dart';
|
||||||
|
|
||||||
|
class FollowList extends StatefulWidget {
|
||||||
|
final FollowController ctr;
|
||||||
|
const FollowList({
|
||||||
|
super.key,
|
||||||
|
required this.ctr,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FollowList> createState() => _FollowListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FollowListState extends State<FollowList> {
|
||||||
|
late Future _futureBuilderFuture;
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_futureBuilderFuture = widget.ctr.queryFollowings('init');
|
||||||
|
scrollController.addListener(
|
||||||
|
() async {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
EasyThrottle.throttle('follow', const Duration(seconds: 1), () {
|
||||||
|
widget.ctr.queryFollowings('onLoad');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
scrollController.removeListener(() {});
|
||||||
|
scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async => await widget.ctr.queryFollowings('init'),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
var data = snapshot.data;
|
||||||
|
if (data['status']) {
|
||||||
|
List<FollowItemModel> list = widget.ctr.followList;
|
||||||
|
return Obx(
|
||||||
|
() => list.isNotEmpty
|
||||||
|
? ListView.builder(
|
||||||
|
controller: scrollController,
|
||||||
|
itemCount: list.length + 1,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
if (index == list.length) {
|
||||||
|
return Container(
|
||||||
|
height:
|
||||||
|
MediaQuery.of(context).padding.bottom + 60,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom:
|
||||||
|
MediaQuery.of(context).padding.bottom),
|
||||||
|
child: Center(
|
||||||
|
child: Obx(
|
||||||
|
() => Text(
|
||||||
|
widget.ctr.loadingText.value,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline,
|
||||||
|
fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return FollowItem(item: list[index]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const CustomScrollView(slivers: [NoData()]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => widget.ctr.queryFollowings('init'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
lib/pages/follow/widgets/owner_follow_list.dart
Normal file
128
lib/pages/follow/widgets/owner_follow_list.dart
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
|
import 'package:pilipala/http/member.dart';
|
||||||
|
import 'package:pilipala/models/follow/result.dart';
|
||||||
|
import 'package:pilipala/models/member/tags.dart';
|
||||||
|
import 'package:pilipala/pages/follow/index.dart';
|
||||||
|
import 'follow_item.dart';
|
||||||
|
|
||||||
|
class OwnerFollowList extends StatefulWidget {
|
||||||
|
final FollowController ctr;
|
||||||
|
final MemberTagItemModel? tagItem;
|
||||||
|
const OwnerFollowList({super.key, required this.ctr, this.tagItem});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OwnerFollowList> createState() => _OwnerFollowListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OwnerFollowListState extends State<OwnerFollowList>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
late int mid;
|
||||||
|
late Future _futureBuilderFuture;
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
int pn = 1;
|
||||||
|
int ps = 20;
|
||||||
|
late MemberTagItemModel tagItem;
|
||||||
|
RxList<FollowItemModel> followList = <FollowItemModel>[].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
mid = widget.ctr.mid;
|
||||||
|
tagItem = widget.tagItem!;
|
||||||
|
_futureBuilderFuture = followUpGroup('init');
|
||||||
|
scrollController.addListener(
|
||||||
|
() async {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
EasyThrottle.throttle('follow', const Duration(seconds: 1), () {
|
||||||
|
followUpGroup('onLoad');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取分组下up
|
||||||
|
Future followUpGroup(type) async {
|
||||||
|
if (type == 'init') {
|
||||||
|
pn = 1;
|
||||||
|
}
|
||||||
|
var res = await MemberHttp.followUpGroup(mid, tagItem.tagid, pn, ps);
|
||||||
|
if (res['status']) {
|
||||||
|
if (res['data'].isNotEmpty) {
|
||||||
|
if (type == 'init') {
|
||||||
|
followList.value = res['data'];
|
||||||
|
} else {
|
||||||
|
followList.addAll(res['data']);
|
||||||
|
}
|
||||||
|
pn += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
scrollController.removeListener(() {});
|
||||||
|
scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async => await followUpGroup('init'),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
var data = snapshot.data;
|
||||||
|
if (data['status']) {
|
||||||
|
return Obx(
|
||||||
|
() => followList.isNotEmpty
|
||||||
|
? ListView.builder(
|
||||||
|
controller: scrollController,
|
||||||
|
itemCount: followList.length + 1,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
if (index == followList.length) {
|
||||||
|
return Container(
|
||||||
|
height:
|
||||||
|
MediaQuery.of(context).padding.bottom + 60,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom:
|
||||||
|
MediaQuery.of(context).padding.bottom),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return FollowItem(item: followList[index]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const CustomScrollView(slivers: [NoData()]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => widget.ctr.queryFollowings('init'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,7 +24,7 @@ class VideoDetailController extends GetxController
|
|||||||
with GetSingleTickerProviderStateMixin {
|
with GetSingleTickerProviderStateMixin {
|
||||||
/// 路由传参
|
/// 路由传参
|
||||||
String bvid = Get.parameters['bvid']!;
|
String bvid = Get.parameters['bvid']!;
|
||||||
int cid = int.parse(Get.parameters['cid']!);
|
RxInt cid = int.parse(Get.parameters['cid']!).obs;
|
||||||
RxInt danmakuCid = 0.obs;
|
RxInt danmakuCid = 0.obs;
|
||||||
String heroTag = Get.arguments['heroTag'];
|
String heroTag = Get.arguments['heroTag'];
|
||||||
// 视频详情
|
// 视频详情
|
||||||
@ -109,7 +109,7 @@ class VideoDetailController extends GetxController
|
|||||||
localCache.get(LocalCacheKey.historyPause) == true) {
|
localCache.get(LocalCacheKey.historyPause) == true) {
|
||||||
enableHeart = false;
|
enableHeart = false;
|
||||||
}
|
}
|
||||||
danmakuCid.value = cid;
|
danmakuCid.value = cid.value;
|
||||||
|
|
||||||
///
|
///
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
@ -218,7 +218,7 @@ class VideoDetailController extends GetxController
|
|||||||
// 默认1倍速
|
// 默认1倍速
|
||||||
speed: 1.0,
|
speed: 1.0,
|
||||||
bvid: bvid,
|
bvid: bvid,
|
||||||
cid: cid,
|
cid: cid.value,
|
||||||
enableHeart: enableHeart,
|
enableHeart: enableHeart,
|
||||||
isFirstTime: isFirstTime,
|
isFirstTime: isFirstTime,
|
||||||
autoplay: autoplay,
|
autoplay: autoplay,
|
||||||
@ -230,7 +230,7 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
// 视频链接
|
// 视频链接
|
||||||
Future queryVideoUrl() async {
|
Future queryVideoUrl() async {
|
||||||
var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid);
|
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
data = result['data'];
|
data = result['data'];
|
||||||
|
|
||||||
|
|||||||
@ -11,11 +11,14 @@ import 'package:pilipala/models/user/fav_folder.dart';
|
|||||||
import 'package:pilipala/models/video_detail_res.dart';
|
import 'package:pilipala/models/video_detail_res.dart';
|
||||||
import 'package:pilipala/pages/video/detail/controller.dart';
|
import 'package:pilipala/pages/video/detail/controller.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.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';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
import 'widgets/group_panel.dart';
|
||||||
|
|
||||||
class VideoIntroController extends GetxController {
|
class VideoIntroController extends GetxController {
|
||||||
// 视频bvid
|
// 视频bvid
|
||||||
String bvid = Get.parameters['bvid']!;
|
String bvid = Get.parameters['bvid']!;
|
||||||
@ -58,6 +61,7 @@ class VideoIntroController extends GetxController {
|
|||||||
RxString total = '1'.obs;
|
RxString total = '1'.obs;
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
bool isPaused = false;
|
bool isPaused = false;
|
||||||
|
String heroTag = Get.arguments['heroTag'];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -102,9 +106,10 @@ class VideoIntroController extends GetxController {
|
|||||||
if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) {
|
if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) {
|
||||||
lastPlayCid.value = videoDetail.value.pages!.first.cid!;
|
lastPlayCid.value = videoDetail.value.pages!.first.cid!;
|
||||||
}
|
}
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
// Get.find<VideoDetailController>(tag: heroTag).tabs.value = [
|
||||||
.tabs
|
// '简介',
|
||||||
.value = ['简介', '评论 ${result['data']!.stat!.reply}'];
|
// '评论 ${result['data']!.stat!.reply}'
|
||||||
|
// ];
|
||||||
// 获取到粉丝数再返回
|
// 获取到粉丝数再返回
|
||||||
await queryUserStat();
|
await queryUserStat();
|
||||||
}
|
}
|
||||||
@ -425,6 +430,20 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
followStatus['attribute'] = actionStatus;
|
followStatus['attribute'] = actionStatus;
|
||||||
followStatus.refresh();
|
followStatus.refresh();
|
||||||
|
if (actionStatus == 2) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: const Text('关注成功'),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: '设置分组',
|
||||||
|
onPressed: setFollowGroup,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
@ -440,16 +459,16 @@ class VideoIntroController extends GetxController {
|
|||||||
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
||||||
// 重新获取视频资源
|
// 重新获取视频资源
|
||||||
VideoDetailController videoDetailCtr =
|
VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
videoDetailCtr.bvid = bvid;
|
videoDetailCtr.bvid = bvid;
|
||||||
videoDetailCtr.cid = cid;
|
videoDetailCtr.cid.value = cid;
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
/// 未渲染回复组件时可能异常
|
/// 未渲染回复组件时可能异常
|
||||||
VideoReplyController videoReplyCtr =
|
VideoReplyController videoReplyCtr =
|
||||||
Get.find<VideoReplyController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoReplyController>(tag: heroTag);
|
||||||
videoReplyCtr.aid = aid;
|
videoReplyCtr.aid = aid;
|
||||||
videoReplyCtr.queryReplyList(type: 'init');
|
videoReplyCtr.queryReplyList(type: 'init');
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -486,4 +505,60 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 列表循环或者顺序播放时,自动播放下一个
|
||||||
|
void nextPlay() {
|
||||||
|
late List episodes;
|
||||||
|
// if (videoDetail.value.ugcSeason != null) {
|
||||||
|
// UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||||
|
// List<SectionItem> sections = ugcSeason.sections!;
|
||||||
|
// for (int i = 0; i < sections.length; i++) {
|
||||||
|
// List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||||
|
// for (int j = 0; j < episodesList.length; j++) {
|
||||||
|
// if (episodesList[j].cid == lastPlayCid.value) {
|
||||||
|
// episodes = episodesList;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (videoDetail.value.ugcSeason != null) {
|
||||||
|
UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||||
|
List<SectionItem> sections = ugcSeason.sections!;
|
||||||
|
episodes = [];
|
||||||
|
|
||||||
|
for (int i = 0; i < sections.length; i++) {
|
||||||
|
List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||||
|
episodes.addAll(episodesList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
||||||
|
int nextIndex = currentIndex + 1;
|
||||||
|
VideoDetailController videoDetailCtr =
|
||||||
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
|
|
||||||
|
// 列表循环
|
||||||
|
if (nextIndex >= episodes.length) {
|
||||||
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
|
nextIndex = 0;
|
||||||
|
}
|
||||||
|
if (platRepeat == PlayRepeat.listOrder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int cid = episodes[nextIndex].cid!;
|
||||||
|
String bvid = episodes[nextIndex].bvid!;
|
||||||
|
int aid = episodes[nextIndex].aid!;
|
||||||
|
changeSeasonOrbangu(bvid, cid, aid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置关注分组
|
||||||
|
void setFollowGroup() {
|
||||||
|
Get.bottomSheet(
|
||||||
|
GroupPanel(mid: videoDetail.value.owner!.mid!),
|
||||||
|
isScrollControlled: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -330,17 +330,17 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 7),
|
const SizedBox(height: 7),
|
||||||
// 点赞收藏转发 布局样式1
|
// 点赞收藏转发 布局样式1
|
||||||
SingleChildScrollView(
|
// SingleChildScrollView(
|
||||||
padding: const EdgeInsets.only(top: 7, bottom: 7),
|
// padding: const EdgeInsets.only(top: 7, bottom: 7),
|
||||||
scrollDirection: Axis.horizontal,
|
// scrollDirection: Axis.horizontal,
|
||||||
child: actionRow(
|
// child: actionRow(
|
||||||
context,
|
// context,
|
||||||
videoIntroController,
|
// videoIntroController,
|
||||||
videoDetailCtr,
|
// videoDetailCtr,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
// 点赞收藏转发 布局样式2
|
// 点赞收藏转发 布局样式2
|
||||||
// actionGrid(context, videoIntroController),
|
actionGrid(context, videoIntroController),
|
||||||
// 合集
|
// 合集
|
||||||
if (!loadingStatus &&
|
if (!loadingStatus &&
|
||||||
widget.videoDetail!.ugcSeason != null) ...[
|
widget.videoDetail!.ugcSeason != null) ...[
|
||||||
@ -458,7 +458,7 @@ 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: (context, constraints) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.only(top: 6, bottom: 10),
|
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,
|
||||||
@ -477,12 +477,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
? widget.videoDetail!.stat!.like!.toString()
|
? widget.videoDetail!.stat!.like!.toString()
|
||||||
: '-'),
|
: '-'),
|
||||||
),
|
),
|
||||||
ActionItem(
|
// ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.clock),
|
// icon: const Icon(FontAwesomeIcons.clock),
|
||||||
onTap: () => videoIntroController.actionShareVideo(),
|
// onTap: () => videoIntroController.actionShareVideo(),
|
||||||
selectStatus: false,
|
// selectStatus: false,
|
||||||
loadingStatus: loadingStatus,
|
// loadingStatus: loadingStatus,
|
||||||
text: '稍后再看'),
|
// text: '稍后再看'),
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.b),
|
icon: const Icon(FontAwesomeIcons.b),
|
||||||
@ -498,22 +498,28 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.star),
|
icon: const Icon(FontAwesomeIcons.star),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||||
// onTap: () => videoIntroController.actionFavVideo(),
|
|
||||||
onTap: () => showFavBottomSheet(),
|
onTap: () => showFavBottomSheet(),
|
||||||
|
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
||||||
selectStatus: videoIntroController.hasFav.value,
|
selectStatus: videoIntroController.hasFav.value,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
text: !loadingStatus
|
text: !loadingStatus
|
||||||
? widget.videoDetail!.stat!.favorite!.toString()
|
? widget.videoDetail!.stat!.favorite!.toString()
|
||||||
: '-'),
|
: '-'),
|
||||||
),
|
),
|
||||||
|
ActionItem(
|
||||||
|
icon: const Icon(FontAwesomeIcons.comment),
|
||||||
|
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
|
||||||
|
selectStatus: false,
|
||||||
|
loadingStatus: loadingStatus,
|
||||||
|
text: !loadingStatus
|
||||||
|
? widget.videoDetail!.stat!.reply!.toString()
|
||||||
|
: '评论'),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||||
onTap: () => videoIntroController.actionShareVideo(),
|
onTap: () => videoIntroController.actionShareVideo(),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
text: !loadingStatus
|
text: '分享'),
|
||||||
? widget.videoDetail!.stat!.share!.toString()
|
|
||||||
: '-'),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,6 +6,7 @@ class ActionItem extends StatelessWidget {
|
|||||||
final Icon? icon;
|
final Icon? icon;
|
||||||
final Icon? selectIcon;
|
final Icon? selectIcon;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
|
final Function? onLongPress;
|
||||||
final bool? loadingStatus;
|
final bool? loadingStatus;
|
||||||
final String? text;
|
final String? text;
|
||||||
final bool selectStatus;
|
final bool selectStatus;
|
||||||
@ -15,6 +16,7 @@ class ActionItem extends StatelessWidget {
|
|||||||
this.icon,
|
this.icon,
|
||||||
this.selectIcon,
|
this.selectIcon,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
this.onLongPress,
|
||||||
this.loadingStatus,
|
this.loadingStatus,
|
||||||
this.text,
|
this.text,
|
||||||
this.selectStatus = false,
|
this.selectStatus = false,
|
||||||
@ -27,6 +29,9 @@ class ActionItem extends StatelessWidget {
|
|||||||
feedBack(),
|
feedBack(),
|
||||||
onTap!(),
|
onTap!(),
|
||||||
},
|
},
|
||||||
|
onLongPress: () => {
|
||||||
|
if (onLongPress != null) {onLongPress!()}
|
||||||
|
},
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|||||||
156
lib/pages/video/detail/introduction/widgets/group_panel.dart
Normal file
156
lib/pages/video/detail/introduction/widgets/group_panel.dart
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/http/member.dart';
|
||||||
|
import 'package:pilipala/models/member/tags.dart';
|
||||||
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
class GroupPanel extends StatefulWidget {
|
||||||
|
final int? mid;
|
||||||
|
const GroupPanel({super.key, this.mid});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<GroupPanel> createState() => _GroupPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GroupPanelState extends State<GroupPanel> {
|
||||||
|
Box localCache = GStrorage.localCache;
|
||||||
|
late double sheetHeight;
|
||||||
|
late Future _futureBuilderFuture;
|
||||||
|
late List<MemberTagItemModel> tagsList;
|
||||||
|
bool showDefault = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
sheetHeight = localCache.get('sheetHeight');
|
||||||
|
_futureBuilderFuture = MemberHttp.followUpTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSave() async {
|
||||||
|
feedBack();
|
||||||
|
// 是否有选中的 有选中的带id,没选使用默认0
|
||||||
|
bool anyHasChecked = tagsList.any((e) => e.checked == true);
|
||||||
|
late String tagids;
|
||||||
|
if (anyHasChecked) {
|
||||||
|
List checkedList = tagsList.where((e) => e.checked == true).toList();
|
||||||
|
List<int> tagidList = checkedList.map<int>((e) => e.tagid).toList();
|
||||||
|
tagids = tagidList.join(',');
|
||||||
|
} else {
|
||||||
|
tagids = '0';
|
||||||
|
}
|
||||||
|
// 保存
|
||||||
|
var res = await MemberHttp.addUsers(widget.mid, tagids);
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
if (res['status']) {
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: sheetHeight,
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AppBar(
|
||||||
|
centerTitle: false,
|
||||||
|
elevation: 0,
|
||||||
|
leading: IconButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
icon: const Icon(Icons.close_outlined)),
|
||||||
|
title:
|
||||||
|
Text('设置关注分组', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Material(
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
Map data = snapshot.data as Map;
|
||||||
|
if (data['status']) {
|
||||||
|
tagsList = data['data'];
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: data['data'].length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ListTile(
|
||||||
|
onTap: () {
|
||||||
|
data['data'][index].checked =
|
||||||
|
!data['data'][index].checked;
|
||||||
|
showDefault =
|
||||||
|
!data['data'].any((e) => e.checked == true);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
dense: true,
|
||||||
|
leading: const Icon(Icons.group_outlined),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
title: Text(data['data'][index].name),
|
||||||
|
subtitle: data['data'][index].tip != ''
|
||||||
|
? Text(data['data'][index].tip)
|
||||||
|
: null,
|
||||||
|
trailing: Transform.scale(
|
||||||
|
scale: 0.9,
|
||||||
|
child: Checkbox(
|
||||||
|
value: data['data'][index].checked,
|
||||||
|
onChanged: (bool? checkValue) {
|
||||||
|
data['data'][index].checked = checkValue;
|
||||||
|
showDefault = !data['data']
|
||||||
|
.any((e) => e.checked == true);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => setState(() {}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return const Text('请求中');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: Theme.of(context).disabledColor.withOpacity(0.08),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
top: 12,
|
||||||
|
bottom: MediaQuery.of(context).padding.bottom + 12,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => onSave(),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.only(left: 30, right: 30),
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).colorScheme.primary, // 设置按钮背景色
|
||||||
|
),
|
||||||
|
child: Text(showDefault ? '保存至默认分组' : '保存'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +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/video_detail_res.dart';
|
import 'package:pilipala/models/video_detail_res.dart';
|
||||||
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
|
|
||||||
class PagesPanel extends StatefulWidget {
|
class PagesPanel extends StatefulWidget {
|
||||||
final List<Part> pages;
|
final List<Part> pages;
|
||||||
@ -22,13 +23,23 @@ class PagesPanel extends StatefulWidget {
|
|||||||
|
|
||||||
class _PagesPanelState extends State<PagesPanel> {
|
class _PagesPanelState extends State<PagesPanel> {
|
||||||
late List<Part> episodes;
|
late List<Part> episodes;
|
||||||
|
late int cid;
|
||||||
late int currentIndex;
|
late int currentIndex;
|
||||||
|
String heroTag = Get.arguments['heroTag'];
|
||||||
|
late VideoDetailController _videoDetailController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
cid = widget.cid!;
|
||||||
episodes = widget.pages;
|
episodes = widget.pages;
|
||||||
currentIndex = episodes.indexWhere((e) => e.cid == widget.cid);
|
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||||
|
_videoDetailController.cid.listen((p0) {
|
||||||
|
cid = p0;
|
||||||
|
setState(() {});
|
||||||
|
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeFucCall(item, i) async {
|
void changeFucCall(item, i) async {
|
||||||
|
|||||||
@ -1,6 +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/video_detail_res.dart';
|
import 'package:pilipala/models/video_detail_res.dart';
|
||||||
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
|
|
||||||
class SeasonPanel extends StatefulWidget {
|
class SeasonPanel extends StatefulWidget {
|
||||||
@ -23,11 +24,16 @@ class SeasonPanel extends StatefulWidget {
|
|||||||
|
|
||||||
class _SeasonPanelState extends State<SeasonPanel> {
|
class _SeasonPanelState extends State<SeasonPanel> {
|
||||||
late List<EpisodeItem> episodes;
|
late List<EpisodeItem> episodes;
|
||||||
|
late int cid;
|
||||||
late int currentIndex;
|
late int currentIndex;
|
||||||
|
String heroTag = Get.arguments['heroTag'];
|
||||||
|
late VideoDetailController _videoDetailController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
cid = widget.cid!;
|
||||||
|
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
|
||||||
/// 根据 cid 找到对应集,找到对应 episodes
|
/// 根据 cid 找到对应集,找到对应 episodes
|
||||||
/// 有多个episodes时,只显示其中一个
|
/// 有多个episodes时,只显示其中一个
|
||||||
@ -36,7 +42,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
for (int i = 0; i < sections.length; i++) {
|
for (int i = 0; i < sections.length; i++) {
|
||||||
List<EpisodeItem> episodesList = sections[i].episodes!;
|
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 == widget.cid) {
|
if (episodesList[j].cid == cid) {
|
||||||
episodes = episodesList;
|
episodes = episodesList;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -47,7 +53,12 @@ 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 == widget.cid);
|
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||||
|
_videoDetailController.cid.listen((p0) {
|
||||||
|
cid = p0;
|
||||||
|
setState(() {});
|
||||||
|
currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeFucCall(item, i) async {
|
void changeFucCall(item, i) async {
|
||||||
@ -57,6 +68,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
item.aid,
|
item.aid,
|
||||||
);
|
);
|
||||||
currentIndex = i;
|
currentIndex = i;
|
||||||
|
setState(() {});
|
||||||
Get.back();
|
Get.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import 'package:pilipala/pages/video/detail/controller.dart';
|
|||||||
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/related/index.dart';
|
import 'package:pilipala/pages/video/detail/related/index.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
import 'widgets/app_bar.dart';
|
import 'widgets/app_bar.dart';
|
||||||
@ -41,6 +42,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
final ScrollController _extendNestCtr = ScrollController();
|
final ScrollController _extendNestCtr = ScrollController();
|
||||||
late StreamController<double> appbarStream;
|
late StreamController<double> appbarStream;
|
||||||
late VideoIntroController videoIntroController;
|
late VideoIntroController videoIntroController;
|
||||||
|
late BangumiIntroController bangumiIntroController;
|
||||||
late String heroTag;
|
late String heroTag;
|
||||||
|
|
||||||
PlayerStatus playerStatus = PlayerStatus.playing;
|
PlayerStatus playerStatus = PlayerStatus.playing;
|
||||||
@ -61,6 +63,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
heroTag = Get.arguments['heroTag'];
|
heroTag = Get.arguments['heroTag'];
|
||||||
videoDetailController = Get.put(VideoDetailController(), tag: heroTag);
|
videoDetailController = Get.put(VideoDetailController(), tag: heroTag);
|
||||||
videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
|
videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
|
||||||
|
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
|
||||||
statusBarHeight = localCache.get('statusBarHeight');
|
statusBarHeight = localCache.get('statusBarHeight');
|
||||||
autoExitFullcreen =
|
autoExitFullcreen =
|
||||||
setting.get(SettingBoxKey.enableAutoExit, defaultValue: false);
|
setting.get(SettingBoxKey.enableAutoExit, defaultValue: false);
|
||||||
@ -98,6 +101,23 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
if (autoExitFullcreen) {
|
if (autoExitFullcreen) {
|
||||||
plPlayerController!.triggerFullScreen(status: false);
|
plPlayerController!.triggerFullScreen(status: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 顺序播放 列表循环
|
||||||
|
if (plPlayerController!.playRepeat != PlayRepeat.pause &&
|
||||||
|
plPlayerController!.playRepeat != PlayRepeat.singleCycle) {
|
||||||
|
if (videoDetailController.videoType == SearchType.video) {
|
||||||
|
videoIntroController.nextPlay();
|
||||||
|
}
|
||||||
|
if (videoDetailController.videoType == SearchType.media_bangumi) {
|
||||||
|
bangumiIntroController.nextPlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 单个循环
|
||||||
|
if (plPlayerController!.playRepeat == PlayRepeat.singleCycle) {
|
||||||
|
plPlayerController!.seekTo(Duration.zero);
|
||||||
|
plPlayerController!.play();
|
||||||
|
}
|
||||||
// 播放完展示控制栏
|
// 播放完展示控制栏
|
||||||
try {
|
try {
|
||||||
PiPStatus currentStatus =
|
PiPStatus currentStatus =
|
||||||
@ -385,8 +405,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
const VideoIntroPanel(),
|
const VideoIntroPanel(),
|
||||||
] else if (videoDetailController.videoType ==
|
] else if (videoDetailController.videoType ==
|
||||||
SearchType.media_bangumi) ...[
|
SearchType.media_bangumi) ...[
|
||||||
BangumiIntroPanel(
|
Obx(() => BangumiIntroPanel(
|
||||||
cid: videoDetailController.cid)
|
cid: videoDetailController.cid.value)),
|
||||||
],
|
],
|
||||||
// if (videoDetailController.videoType ==
|
// if (videoDetailController.videoType ==
|
||||||
// SearchType.video) ...[
|
// SearchType.video) ...[
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import 'package:pilipala/models/video/play/url.dart';
|
|||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
|
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||||
@ -56,7 +57,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 400,
|
height: 440,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
@ -149,13 +150,14 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
'当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}',
|
'当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}',
|
||||||
style: subTitleStyle),
|
style: subTitleStyle),
|
||||||
),
|
),
|
||||||
// ListTile(
|
ListTile(
|
||||||
// onTap: () {},
|
onTap: () => {Get.back(), showSetRepeat()},
|
||||||
// dense: true,
|
dense: true,
|
||||||
// enabled: false,
|
leading: const Icon(Icons.repeat, size: 20),
|
||||||
// leading: const Icon(Icons.play_circle_outline, size: 20),
|
title: Text('播放顺序', style: titleStyle),
|
||||||
// title: Text('播放设置', style: titleStyle),
|
subtitle: Text(widget.controller!.playRepeat.description,
|
||||||
// ),
|
style: subTitleStyle),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => {Get.back(), showSetDanmaku()},
|
onTap: () => {Get.back(), showSetDanmaku()},
|
||||||
dense: true,
|
dense: true,
|
||||||
@ -704,6 +706,60 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 播放顺序
|
||||||
|
void showSetRepeat() async {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 250,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 45,
|
||||||
|
child: Center(child: Text('选择播放顺序', style: titleStyle))),
|
||||||
|
Expanded(
|
||||||
|
child: Material(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
for (var i in PlayRepeat.values) ...[
|
||||||
|
ListTile(
|
||||||
|
onTap: () {
|
||||||
|
widget.controller!.setPlayRepeat(i);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
dense: true,
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.only(left: 20, right: 20),
|
||||||
|
title: Text(i.description),
|
||||||
|
trailing: widget.controller!.playRepeat == i
|
||||||
|
? Icon(
|
||||||
|
Icons.done,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final _ = widget.controller!;
|
final _ = widget.controller!;
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import 'package:media_kit_video/media_kit_video.dart';
|
|||||||
import 'package:ns_danmaku/ns_danmaku.dart';
|
import 'package:ns_danmaku/ns_danmaku.dart';
|
||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.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';
|
||||||
@ -209,6 +210,9 @@ class PlPlayerController {
|
|||||||
late double fontSizeVal;
|
late double fontSizeVal;
|
||||||
late double danmakuSpeedVal;
|
late double danmakuSpeedVal;
|
||||||
|
|
||||||
|
// 播放顺序相关
|
||||||
|
PlayRepeat playRepeat = PlayRepeat.pause;
|
||||||
|
|
||||||
// 添加一个私有构造函数
|
// 添加一个私有构造函数
|
||||||
PlPlayerController._() {
|
PlPlayerController._() {
|
||||||
_videoType = videoType;
|
_videoType = videoType;
|
||||||
@ -226,6 +230,12 @@ class PlPlayerController {
|
|||||||
// 弹幕速度
|
// 弹幕速度
|
||||||
danmakuSpeedVal =
|
danmakuSpeedVal =
|
||||||
localCache.get(LocalCacheKey.danmakuSpeed, defaultValue: 4.0);
|
localCache.get(LocalCacheKey.danmakuSpeed, defaultValue: 4.0);
|
||||||
|
playRepeat = PlayRepeat.values.toList().firstWhere(
|
||||||
|
(e) =>
|
||||||
|
e.value ==
|
||||||
|
videoStorage.get(VideoBoxKey.playRepeat,
|
||||||
|
defaultValue: PlayRepeat.pause.value),
|
||||||
|
);
|
||||||
// _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) {
|
// _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) {
|
||||||
// if (status == PlayerStatus.playing) {
|
// if (status == PlayerStatus.playing) {
|
||||||
// WakelockPlus.enable();
|
// WakelockPlus.enable();
|
||||||
@ -910,6 +920,11 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPlayRepeat(PlayRepeat type) {
|
||||||
|
playRepeat = type;
|
||||||
|
videoStorage.put(VideoBoxKey.playRepeat, type.value);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> dispose({String type = 'single'}) async {
|
Future<void> dispose({String type = 'single'}) async {
|
||||||
// 每次减1,最后销毁
|
// 每次减1,最后销毁
|
||||||
if (type == 'single' && playerCount.value > 1) {
|
if (type == 'single' && playerCount.value > 1) {
|
||||||
|
|||||||
25
lib/plugin/pl_player/models/play_repeat.dart
Normal file
25
lib/plugin/pl_player/models/play_repeat.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
enum PlayRepeat {
|
||||||
|
pause,
|
||||||
|
listOrder,
|
||||||
|
singleCycle,
|
||||||
|
listCycle,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PlayRepeatExtension on PlayRepeat {
|
||||||
|
static final List<String> _descList = [
|
||||||
|
'播完暂停',
|
||||||
|
'顺序播放',
|
||||||
|
'单个循环',
|
||||||
|
'列表循环',
|
||||||
|
];
|
||||||
|
get description => _descList[index];
|
||||||
|
|
||||||
|
static final List<double> _valueList = [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
];
|
||||||
|
get value => _valueList[index];
|
||||||
|
get defaultValue => _valueList[1];
|
||||||
|
}
|
||||||
@ -158,4 +158,6 @@ class VideoBoxKey {
|
|||||||
static const String videoBrightness = 'videoBrightness';
|
static const String videoBrightness = 'videoBrightness';
|
||||||
// 倍速
|
// 倍速
|
||||||
static const String videoSpeed = 'videoSpeed';
|
static const String videoSpeed = 'videoSpeed';
|
||||||
|
// 播放顺序
|
||||||
|
static const String playRepeat = 'playRepeat';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user