Merge branch 'main' into fix
This commit is contained in:
BIN
assets/images/pay/alipay.jpg
Normal file
BIN
assets/images/pay/alipay.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 143 KiB |
BIN
assets/images/pay/wechat.png
Normal file
BIN
assets/images/pay/wechat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
@ -15,4 +15,5 @@ class Constants {
|
|||||||
// 59b43e04ad6965f34319062b478f83dd TV端
|
// 59b43e04ad6965f34319062b478f83dd TV端
|
||||||
static const String appSec = '59b43e04ad6965f34319062b478f83dd';
|
static const String appSec = '59b43e04ad6965f34319062b478f83dd';
|
||||||
static const String thirdSign = '04224646d1fea004e79606d3b038c84a';
|
static const String thirdSign = '04224646d1fea004e79606d3b038c84a';
|
||||||
|
static const List<int> publicFavFolder = <int>[0, 2, 22];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -430,7 +430,7 @@ class EpisodeGridItem extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isCurrentIndex
|
color: isCurrentIndex
|
||||||
? colorScheme.primaryContainer.withOpacity(0.6)
|
? colorScheme.primaryContainer.withOpacity(0.6)
|
||||||
: colorScheme.secondaryContainer.withOpacity(0.4),
|
: colorScheme.onInverseSurface.withOpacity(0.6),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isCurrentIndex
|
color: isCurrentIndex
|
||||||
|
|||||||
@ -3,14 +3,9 @@ import 'package:pilipala/common/constants.dart';
|
|||||||
|
|
||||||
import 'skeleton.dart';
|
import 'skeleton.dart';
|
||||||
|
|
||||||
class MediaBangumiSkeleton extends StatefulWidget {
|
class MediaBangumiSkeleton extends StatelessWidget {
|
||||||
const MediaBangumiSkeleton({super.key});
|
const MediaBangumiSkeleton({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<MediaBangumiSkeleton> createState() => _MediaBangumiSkeletonState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MediaBangumiSkeletonState extends State<MediaBangumiSkeleton> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
|
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
|
||||||
@ -35,25 +30,25 @@ class _MediaBangumiSkeletonState extends State<MediaBangumiSkeleton> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: bgColor,
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 20,
|
height: 20,
|
||||||
margin: const EdgeInsets.only(bottom: 15),
|
margin: const EdgeInsets.only(bottom: 15),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: bgColor,
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 13,
|
height: 13,
|
||||||
margin: const EdgeInsets.only(bottom: 5),
|
margin: const EdgeInsets.only(bottom: 5),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: bgColor,
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 13,
|
height: 13,
|
||||||
margin: const EdgeInsets.only(bottom: 5),
|
margin: const EdgeInsets.only(bottom: 5),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: bgColor,
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 13,
|
height: 13,
|
||||||
),
|
),
|
||||||
@ -64,7 +59,7 @@ class _MediaBangumiSkeletonState extends State<MediaBangumiSkeleton> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
const BorderRadius.all(Radius.circular(20)),
|
const BorderRadius.all(Radius.circular(20)),
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: bgColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
42
lib/common/skeleton/user_list.dart
Normal file
42
lib/common/skeleton/user_list.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../constants.dart';
|
||||||
|
|
||||||
|
class UserListSkeleton extends StatelessWidget {
|
||||||
|
const UserListSkeleton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: StyleString.safeSpace, vertical: 7),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ClipOval(
|
||||||
|
child: Container(width: 42, height: 42, color: bgColor),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(color: bgColor, width: 60, height: 13),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Container(color: bgColor, width: 40, height: 13),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Container(
|
||||||
|
color: bgColor,
|
||||||
|
width: 100,
|
||||||
|
height: 13,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
class HttpError extends StatelessWidget {
|
class HttpError extends StatelessWidget {
|
||||||
const HttpError({
|
const HttpError({
|
||||||
required this.errMsg,
|
required this.errMsg,
|
||||||
required this.fn,
|
this.fn,
|
||||||
this.btnText,
|
this.btnText,
|
||||||
this.isShowBtn = true,
|
this.isShowBtn = true,
|
||||||
this.isInSliver = true,
|
this.isInSliver = true,
|
||||||
@ -23,7 +23,6 @@ class HttpError extends StatelessWidget {
|
|||||||
final errorContent = SizedBox(
|
final errorContent = SizedBox(
|
||||||
height: 400,
|
height: 400,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset("assets/images/error.svg", height: 200),
|
SvgPicture.asset("assets/images/error.svg", height: 200),
|
||||||
@ -50,7 +49,7 @@ class HttpError extends StatelessWidget {
|
|||||||
if (isInSliver) {
|
if (isInSliver) {
|
||||||
return SliverToBoxAdapter(child: errorContent);
|
return SliverToBoxAdapter(child: errorContent);
|
||||||
} else {
|
} else {
|
||||||
return Center(child: errorContent);
|
return Align(alignment: Alignment.topCenter, child: errorContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -301,10 +301,6 @@ class Api {
|
|||||||
static const String bangumiList =
|
static const String bangumiList =
|
||||||
'/pgc/season/index/result?st=1&order=3&season_version=-1&spoken_language_type=-1&area=-1&is_finish=-1©right=-1&season_status=-1&season_month=-1&year=-1&style_id=-1&sort=0&season_type=1&pagesize=20&type=1';
|
'/pgc/season/index/result?st=1&order=3&season_version=-1&spoken_language_type=-1&area=-1&is_finish=-1©right=-1&season_status=-1&season_month=-1&year=-1&style_id=-1&sort=0&season_type=1&pagesize=20&type=1';
|
||||||
|
|
||||||
// 我的订阅
|
|
||||||
static const String bangumiFollow =
|
|
||||||
'/x/space/bangumi/follow/list?type=1&follow_status=0&pn=1&ps=15&ts=1691544359969';
|
|
||||||
|
|
||||||
// 黑名单
|
// 黑名单
|
||||||
static const String blackLst = '/x/relation/blacks';
|
static const String blackLst = '/x/relation/blacks';
|
||||||
|
|
||||||
@ -604,4 +600,10 @@ class Api {
|
|||||||
|
|
||||||
/// 图片上传
|
/// 图片上传
|
||||||
static const String uploadImage = '/x/dynamic/feed/draw/upload_bfs';
|
static const String uploadImage = '/x/dynamic/feed/draw/upload_bfs';
|
||||||
|
|
||||||
|
/// 更新追番状态
|
||||||
|
static const String updateBangumiStatus = '/pgc/web/follow/status/update';
|
||||||
|
|
||||||
|
/// 番剧点赞投币收藏状态
|
||||||
|
static const String bangumiActionStatus = '/pgc/season/episode/community';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import '../models/bangumi/list.dart';
|
import '../models/bangumi/list.dart';
|
||||||
import 'index.dart';
|
import 'index.dart';
|
||||||
|
import 'package:html/parser.dart' as html_parser;
|
||||||
|
import 'package:html/dom.dart' as html_dom;
|
||||||
|
|
||||||
class BangumiHttp {
|
class BangumiHttp {
|
||||||
static Future bangumiList({int? page}) async {
|
static Future bangumiList({int? page}) async {
|
||||||
@ -18,8 +21,19 @@ class BangumiHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future bangumiFollow({int? mid}) async {
|
static Future getRecentBangumi({
|
||||||
var res = await Request().get(Api.bangumiFollow, data: {'vmid': mid});
|
int? mid,
|
||||||
|
int type = 1,
|
||||||
|
int pn = 1,
|
||||||
|
int ps = 20,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().get(Api.getRecentBangumiApi, data: {
|
||||||
|
'vmid': mid,
|
||||||
|
'type': type,
|
||||||
|
'follow_status': 0,
|
||||||
|
'pn': pn,
|
||||||
|
'ps': ps,
|
||||||
|
});
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
'status': true,
|
'status': true,
|
||||||
@ -33,4 +47,62 @@ class BangumiHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取追番状态
|
||||||
|
static Future bangumiStatus({required int seasonId}) async {
|
||||||
|
var res = await Request()
|
||||||
|
.get('https://www.bilibili.com/bangumi/play/ss$seasonId');
|
||||||
|
html_dom.Document document = html_parser.parse(res.data);
|
||||||
|
// 查找 id 为 __NEXT_DATA__ 的 script 元素
|
||||||
|
html_dom.Element? scriptElement =
|
||||||
|
document.querySelector('script#\\__NEXT_DATA__');
|
||||||
|
if (scriptElement != null) {
|
||||||
|
// 提取 script 元素的内容
|
||||||
|
String scriptContent = scriptElement.text;
|
||||||
|
final dynamic scriptContentJson = jsonDecode(scriptContent);
|
||||||
|
Map followState = scriptContentJson['props']['pageProps']['followState'];
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': {
|
||||||
|
'isFollowed': followState['isFollowed'],
|
||||||
|
'followStatus': followState['followStatus']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
print('Script element with id "__NEXT_DATA__" not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新追番状态
|
||||||
|
static Future updateBangumiStatus({
|
||||||
|
required int seasonId,
|
||||||
|
required int status,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().post(Api.updateBangumiStatus, data: {
|
||||||
|
'season_id': seasonId,
|
||||||
|
'status': status,
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取番剧点赞投币收藏状态
|
||||||
|
static Future bangumiActionStatus({required int epId}) async {
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.bangumiActionStatus,
|
||||||
|
data: {'ep_id': epId},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class BangumiListItemModel {
|
|||||||
this.title,
|
this.title,
|
||||||
this.titleIcon,
|
this.titleIcon,
|
||||||
this.progress,
|
this.progress,
|
||||||
|
this.progressIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
String? badge;
|
String? badge;
|
||||||
@ -66,8 +67,8 @@ class BangumiListItemModel {
|
|||||||
String? subTitle;
|
String? subTitle;
|
||||||
String? title;
|
String? title;
|
||||||
String? titleIcon;
|
String? titleIcon;
|
||||||
|
|
||||||
String? progress;
|
String? progress;
|
||||||
|
int? progressIndex;
|
||||||
|
|
||||||
BangumiListItemModel.fromJson(Map<String, dynamic> json) {
|
BangumiListItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
badge = json['badge'] == '' ? null : json['badge'];
|
badge = json['badge'] == '' ? null : json['badge'];
|
||||||
@ -87,7 +88,9 @@ class BangumiListItemModel {
|
|||||||
subTitle = json['sub_title'];
|
subTitle = json['sub_title'];
|
||||||
title = json['title'];
|
title = json['title'];
|
||||||
titleIcon = json['title_icon'];
|
titleIcon = json['title_icon'];
|
||||||
|
|
||||||
progress = json['progress'];
|
progress = json['progress'];
|
||||||
|
progressIndex = int.parse(
|
||||||
|
RegExp(r'第(\d+)话').firstMatch(json['progress'] ?? '第1话')?.group(1) ??
|
||||||
|
'0');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,7 +63,7 @@ class LiveFollowingItemModel {
|
|||||||
String? roomNews;
|
String? roomNews;
|
||||||
String? watchIcon;
|
String? watchIcon;
|
||||||
String? textSmall;
|
String? textSmall;
|
||||||
String? roomCover;
|
String? cover;
|
||||||
String? pic;
|
String? pic;
|
||||||
int? parentAreaId;
|
int? parentAreaId;
|
||||||
int? areaId;
|
int? areaId;
|
||||||
@ -90,7 +90,7 @@ class LiveFollowingItemModel {
|
|||||||
this.roomNews,
|
this.roomNews,
|
||||||
this.watchIcon,
|
this.watchIcon,
|
||||||
this.textSmall,
|
this.textSmall,
|
||||||
this.roomCover,
|
this.cover,
|
||||||
this.pic,
|
this.pic,
|
||||||
this.parentAreaId,
|
this.parentAreaId,
|
||||||
this.areaId,
|
this.areaId,
|
||||||
@ -108,7 +108,8 @@ class LiveFollowingItemModel {
|
|||||||
isAttention = json['is_attention'];
|
isAttention = json['is_attention'];
|
||||||
clipNum = json['clipnum'];
|
clipNum = json['clipnum'];
|
||||||
fansNum = json['fans_num'];
|
fansNum = json['fans_num'];
|
||||||
areaName = json['area_name'];
|
areaName =
|
||||||
|
json['area_name'] == '' ? json['area_name_v2'] : json['area_name'];
|
||||||
areaValue = json['area_value'];
|
areaValue = json['area_value'];
|
||||||
tags = json['tags'];
|
tags = json['tags'];
|
||||||
recentRecordIdV2 = json['recent_record_id_v2'];
|
recentRecordIdV2 = json['recent_record_id_v2'];
|
||||||
@ -118,7 +119,7 @@ class LiveFollowingItemModel {
|
|||||||
roomNews = json['room_news'];
|
roomNews = json['room_news'];
|
||||||
watchIcon = json['watch_icon'];
|
watchIcon = json['watch_icon'];
|
||||||
textSmall = json['text_small'];
|
textSmall = json['text_small'];
|
||||||
roomCover = json['room_cover'];
|
cover = json['room_cover'];
|
||||||
pic = json['room_cover'];
|
pic = json['room_cover'];
|
||||||
parentAreaId = json['parent_area_id'];
|
parentAreaId = json['parent_area_id'];
|
||||||
areaId = json['area_id'];
|
areaId = json['area_id'];
|
||||||
|
|||||||
@ -8,6 +8,7 @@ class MemberInfoModel {
|
|||||||
this.level,
|
this.level,
|
||||||
this.isFollowed,
|
this.isFollowed,
|
||||||
this.topPhoto,
|
this.topPhoto,
|
||||||
|
this.silence,
|
||||||
this.official,
|
this.official,
|
||||||
this.vip,
|
this.vip,
|
||||||
this.liveRoom,
|
this.liveRoom,
|
||||||
@ -21,6 +22,7 @@ class MemberInfoModel {
|
|||||||
int? level;
|
int? level;
|
||||||
bool? isFollowed;
|
bool? isFollowed;
|
||||||
String? topPhoto;
|
String? topPhoto;
|
||||||
|
int? silence;
|
||||||
Map? official;
|
Map? official;
|
||||||
Vip? vip;
|
Vip? vip;
|
||||||
LiveRoom? liveRoom;
|
LiveRoom? liveRoom;
|
||||||
@ -34,6 +36,7 @@ class MemberInfoModel {
|
|||||||
level = json['level'];
|
level = json['level'];
|
||||||
isFollowed = json['is_followed'];
|
isFollowed = json['is_followed'];
|
||||||
topPhoto = json['top_photo'];
|
topPhoto = json['top_photo'];
|
||||||
|
silence = json['silence'] ?? 0;
|
||||||
official = json['official'];
|
official = json['official'];
|
||||||
vip = Vip.fromJson(json['vip']);
|
vip = Vip.fromJson(json['vip']);
|
||||||
liveRoom =
|
liveRoom =
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:pilipala/http/index.dart';
|
import 'package:pilipala/http/index.dart';
|
||||||
import 'package:pilipala/models/github/latest.dart';
|
import 'package:pilipala/models/github/latest.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_gallery/index.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import '../../utils/cache_manage.dart';
|
import '../../utils/cache_manage.dart';
|
||||||
@ -124,7 +125,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
onTap: () => _aboutController.webSiteUrl(),
|
onTap: () => _aboutController.webSiteUrl(),
|
||||||
title: const Text('访问官网'),
|
title: const Text('访问官网'),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'https://pilipalanet.mysxl.cn',
|
'https://pilipala.life',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -168,7 +169,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
onTap: () => _aboutController.tgChanel(),
|
onTap: () => _aboutController.tgChanel(),
|
||||||
title: const Text('TG频道'),
|
title: const Text('TG频道'),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'https://t.me/+lm_oOVmF0RJiODk1',
|
'https://t.me/+1DFtqS6usUM5MDNl',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -321,29 +322,35 @@ class AboutController extends GetxController {
|
|||||||
// tg频道
|
// tg频道
|
||||||
tgChanel() {
|
tgChanel() {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
const ClipboardData(text: 'https://t.me/+lm_oOVmF0RJiODk1'),
|
const ClipboardData(text: 'https://t.me/+1DFtqS6usUM5MDNl'),
|
||||||
);
|
);
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast(
|
||||||
'已复制,即将在浏览器打开',
|
'已复制,即将在浏览器打开',
|
||||||
displayTime: const Duration(milliseconds: 500),
|
displayTime: const Duration(milliseconds: 500),
|
||||||
).then(
|
).then(
|
||||||
(value) => launchUrl(
|
(value) => launchUrl(
|
||||||
Uri.parse('https://t.me/+lm_oOVmF0RJiODk1'),
|
Uri.parse('https://t.me/+1DFtqS6usUM5MDNl'),
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
aPay() {
|
aPay() {
|
||||||
try {
|
const List<String> sources = [
|
||||||
launchUrl(
|
'assets/images/pay/wechat.png',
|
||||||
Uri.parse(
|
'assets/images/pay/alipay.jpg'
|
||||||
'alipayqr://platformapi/startapp?saId=10000007&qrcode=https://qr.alipay.com/fkx14623ddwl1ping3ddd73'),
|
];
|
||||||
mode: LaunchMode.externalApplication,
|
Navigator.of(Get.context!).push(
|
||||||
|
HeroDialogRoute<void>(
|
||||||
|
builder: (BuildContext context) => InteractiveviewerGallery(
|
||||||
|
sources: sources,
|
||||||
|
initIndex: 0,
|
||||||
|
itemBuilder: (context, index, isFocus, enablePageView) =>
|
||||||
|
Image.asset(sources[index]),
|
||||||
|
actionType: const [ImgActionType.save],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 官网
|
// 官网
|
||||||
|
|||||||
@ -9,6 +9,7 @@ class BangumiController extends GetxController {
|
|||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
RxList<BangumiListItemModel> bangumiList = <BangumiListItemModel>[].obs;
|
RxList<BangumiListItemModel> bangumiList = <BangumiListItemModel>[].obs;
|
||||||
RxList<BangumiListItemModel> bangumiFollowList = <BangumiListItemModel>[].obs;
|
RxList<BangumiListItemModel> bangumiFollowList = <BangumiListItemModel>[].obs;
|
||||||
|
RxInt total = 0.obs;
|
||||||
int _currentPage = 1;
|
int _currentPage = 1;
|
||||||
bool isLoadingMore = true;
|
bool isLoadingMore = true;
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
@ -54,9 +55,10 @@ class BangumiController extends GetxController {
|
|||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var result = await BangumiHttp.bangumiFollow(mid: userInfo.mid);
|
var result = await BangumiHttp.getRecentBangumi(mid: userInfo.mid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
bangumiFollowList.value = result['data'].list;
|
bangumiFollowList.value = result['data'].list;
|
||||||
|
total.value = result['data'].total;
|
||||||
} else {}
|
} else {}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ 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/bangumi.dart';
|
||||||
import 'package:pilipala/http/constants.dart';
|
import 'package:pilipala/http/constants.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
@ -52,28 +53,34 @@ class BangumiIntroController extends GetxController {
|
|||||||
Rx<FavFolderData> favFolderData = FavFolderData().obs;
|
Rx<FavFolderData> favFolderData = FavFolderData().obs;
|
||||||
List addMediaIdsNew = [];
|
List addMediaIdsNew = [];
|
||||||
List delMediaIdsNew = [];
|
List delMediaIdsNew = [];
|
||||||
// 关注状态 默认未关注
|
// 追番状态 1想看 2在看 3已看
|
||||||
RxMap followStatus = {}.obs;
|
RxBool isFollowed = false.obs;
|
||||||
|
RxInt followStatus = 1.obs;
|
||||||
int _tempThemeValue = -1;
|
int _tempThemeValue = -1;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
PersistentBottomSheetController? bottomSheetController;
|
PersistentBottomSheetController? bottomSheetController;
|
||||||
|
List<Map<String, dynamic>> followStatusList = [
|
||||||
|
{'title': '标记为 「想看」', 'status': 1},
|
||||||
|
{'title': '标记为 「在看」', 'status': 2},
|
||||||
|
{'title': '标记为 「已看」', 'status': 3},
|
||||||
|
{'title': '取消追番', 'status': -1},
|
||||||
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
userLogin = userInfo != null;
|
userLogin = userInfo != null;
|
||||||
|
if (userLogin && seasonId != null) {
|
||||||
|
bangumiStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取番剧简介&选集
|
// 获取番剧简介&选集
|
||||||
Future queryBangumiIntro() async {
|
Future queryBangumiIntro() async {
|
||||||
if (userLogin) {
|
if (userLogin) {
|
||||||
// 获取点赞状态
|
// 获取点赞投币收藏状态
|
||||||
queryHasLikeVideo();
|
bangumiActionStatus();
|
||||||
// 获取投币状态
|
|
||||||
queryHasCoinVideo();
|
|
||||||
// 获取收藏状态
|
|
||||||
queryHasFavVideo();
|
|
||||||
}
|
}
|
||||||
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId);
|
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
@ -83,26 +90,15 @@ class BangumiIntroController extends GetxController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取点赞状态
|
// 获取番剧点赞投币收藏状态
|
||||||
Future queryHasLikeVideo() async {
|
Future bangumiActionStatus() async {
|
||||||
var result = await VideoHttp.hasLikeVideo(bvid: bvid);
|
var result = await BangumiHttp.bangumiActionStatus(epId: epId!);
|
||||||
// data num 被点赞标志 0:未点赞 1:已点赞
|
|
||||||
hasLike.value = result["data"] == 1 ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取投币状态
|
|
||||||
Future queryHasCoinVideo() async {
|
|
||||||
var result = await VideoHttp.hasCoinVideo(bvid: bvid);
|
|
||||||
hasCoin.value = result["data"]['multiply'] == 0 ? false : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取收藏状态
|
|
||||||
Future queryHasFavVideo() async {
|
|
||||||
var result = await VideoHttp.hasFavVideo(aid: IdUtils.bv2av(bvid));
|
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
hasFav.value = result["data"]['favoured'];
|
hasLike.value = result['data']['like'] == 1;
|
||||||
|
hasCoin.value = result['data']['coin_number'] != 0;
|
||||||
|
hasFav.value = result['data']['favorite'] == 1;
|
||||||
} else {
|
} else {
|
||||||
hasFav.value = false;
|
SmartDialog.showToast(result['msg']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +106,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
Future actionLikeVideo() async {
|
Future actionLikeVideo() async {
|
||||||
var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value);
|
var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
SmartDialog.showToast(!hasLike.value ? '点赞成功 👍' : '取消赞');
|
SmartDialog.showToast(!hasLike.value ? '点赞成功' : '取消赞');
|
||||||
hasLike.value = !hasLike.value;
|
hasLike.value = !hasLike.value;
|
||||||
bangumiDetail.value.stat!['likes'] =
|
bangumiDetail.value.stat!['likes'] =
|
||||||
bangumiDetail.value.stat!['likes'] + (!hasLike.value ? 1 : -1);
|
bangumiDetail.value.stat!['likes'] + (!hasLike.value ? 1 : -1);
|
||||||
@ -147,7 +143,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
var res = await VideoHttp.coinVideo(
|
var res = await VideoHttp.coinVideo(
|
||||||
bvid: bvid, multiply: _tempThemeValue);
|
bvid: bvid, multiply: _tempThemeValue);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
SmartDialog.showToast('投币成功 👏');
|
SmartDialog.showToast('投币成功');
|
||||||
hasCoin.value = true;
|
hasCoin.value = true;
|
||||||
bangumiDetail.value.stat!['coins'] =
|
bangumiDetail.value.stat!['coins'] =
|
||||||
bangumiDetail.value.stat!['coins'] +
|
bangumiDetail.value.stat!['coins'] +
|
||||||
@ -185,9 +181,11 @@ class BangumiIntroController extends GetxController {
|
|||||||
addMediaIdsNew = [];
|
addMediaIdsNew = [];
|
||||||
delMediaIdsNew = [];
|
delMediaIdsNew = [];
|
||||||
// 重新获取收藏状态
|
// 重新获取收藏状态
|
||||||
queryHasFavVideo();
|
bangumiActionStatus();
|
||||||
SmartDialog.showToast('✅ 操作成功');
|
SmartDialog.showToast('操作成功');
|
||||||
Get.back();
|
Get.back();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,15 +237,22 @@ class BangumiIntroController extends GetxController {
|
|||||||
|
|
||||||
// 追番
|
// 追番
|
||||||
Future bangumiAdd() async {
|
Future bangumiAdd() async {
|
||||||
var result =
|
var result = await VideoHttp.bangumiAdd(
|
||||||
await VideoHttp.bangumiAdd(seasonId: bangumiDetail.value.seasonId);
|
seasonId: seasonId ?? bangumiDetail.value.seasonId);
|
||||||
|
if (result['status']) {
|
||||||
|
followStatus.value = 2;
|
||||||
|
isFollowed.value = true;
|
||||||
|
}
|
||||||
SmartDialog.showToast(result['msg']);
|
SmartDialog.showToast(result['msg']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消追番
|
// 取消追番
|
||||||
Future bangumiDel() async {
|
Future bangumiDel() async {
|
||||||
var result =
|
var result = await VideoHttp.bangumiDel(
|
||||||
await VideoHttp.bangumiDel(seasonId: bangumiDetail.value.seasonId);
|
seasonId: seasonId ?? bangumiDetail.value.seasonId);
|
||||||
|
if (result['status']) {
|
||||||
|
isFollowed.value = false;
|
||||||
|
}
|
||||||
SmartDialog.showToast(result['msg']);
|
SmartDialog.showToast(result['msg']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,4 +320,35 @@ class BangumiIntroController extends GetxController {
|
|||||||
hiddenEpisodeBottomSheet() {
|
hiddenEpisodeBottomSheet() {
|
||||||
bottomSheetController?.close();
|
bottomSheetController?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取追番状态
|
||||||
|
Future bangumiStatus() async {
|
||||||
|
var result = await BangumiHttp.bangumiStatus(seasonId: seasonId!);
|
||||||
|
if (result['status']) {
|
||||||
|
followStatus.value = result['data']['followStatus'];
|
||||||
|
isFollowed.value = result['data']['isFollowed'];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新追番状态
|
||||||
|
Future updateBangumiStatus(int status) async {
|
||||||
|
Get.back();
|
||||||
|
if (status == -1) {
|
||||||
|
bangumiDel();
|
||||||
|
} else {
|
||||||
|
var result = await BangumiHttp.bangumiStatus(seasonId: seasonId!);
|
||||||
|
if (result['status']) {
|
||||||
|
followStatus.value = status;
|
||||||
|
final title = followStatusList.firstWhere(
|
||||||
|
(e) => e['status'] == status,
|
||||||
|
orElse: () => {'title': '未知状态'},
|
||||||
|
)['title'];
|
||||||
|
SmartDialog.showToast('追番状态$title');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -239,51 +239,40 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => showIntroDetail(),
|
onTap: () => showIntroDetail(),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 115 / 0.75,
|
height: 115 / 0.75,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(6, 4, 6, 6),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.bangumiDetail!.title!,
|
widget.bangumiDetail!.title!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
SizedBox(
|
Obx(
|
||||||
width: 34,
|
() => BangumiStatusWidget(
|
||||||
height: 34,
|
ctr: bangumiIntroController,
|
||||||
child: IconButton(
|
isFollowed:
|
||||||
style: ButtonStyle(
|
bangumiIntroController.isFollowed.value,
|
||||||
padding: MaterialStateProperty.all(
|
|
||||||
EdgeInsets.zero),
|
|
||||||
backgroundColor:
|
|
||||||
MaterialStateProperty.resolveWith(
|
|
||||||
(Set<MaterialState> states) {
|
|
||||||
return t.colorScheme.primaryContainer
|
|
||||||
.withOpacity(0.7);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
onPressed: () =>
|
|
||||||
bangumiIntroController.bangumiAdd(),
|
|
||||||
icon: Icon(
|
|
||||||
Icons.favorite_border_rounded,
|
|
||||||
color: t.colorScheme.primary,
|
|
||||||
size: 22,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
@ -297,42 +286,13 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 10),
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
(widget.bangumiDetail!.areas!.isNotEmpty
|
|
||||||
? widget.bangumiDetail!.areas!.first['name']
|
|
||||||
: ''),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: t.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
widget.bangumiDetail!.publish!['pub_time_show'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: t.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
widget.bangumiDetail!.newEp!['desc'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: t.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Text(
|
Text(
|
||||||
'简介:${widget.bangumiDetail!.evaluate!}',
|
'简介:${widget.bangumiDetail!.evaluate!}',
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 14,
|
||||||
color: t.colorScheme.outline,
|
color: t.colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -341,6 +301,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
@ -426,3 +387,97 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 追番状态
|
||||||
|
class BangumiStatusWidget extends StatelessWidget {
|
||||||
|
final BangumiIntroController ctr;
|
||||||
|
final bool isFollowed;
|
||||||
|
|
||||||
|
const BangumiStatusWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.ctr,
|
||||||
|
required this.isFollowed,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
ColorScheme colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
void updateFollowStatus() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) {
|
||||||
|
return morePanel(context, ctr);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Obx(
|
||||||
|
() => SizedBox(
|
||||||
|
width: 34,
|
||||||
|
height: 34,
|
||||||
|
child: IconButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
return ctr.isFollowed.value
|
||||||
|
? colorScheme.primaryContainer.withOpacity(0.7)
|
||||||
|
: colorScheme.outlineVariant.withOpacity(0.7);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
onPressed:
|
||||||
|
isFollowed ? () => updateFollowStatus() : () => ctr.bangumiAdd(),
|
||||||
|
icon: Icon(
|
||||||
|
ctr.isFollowed.value
|
||||||
|
? Icons.favorite
|
||||||
|
: Icons.favorite_border_rounded,
|
||||||
|
color: ctr.isFollowed.value
|
||||||
|
? colorScheme.primary
|
||||||
|
: colorScheme.outline,
|
||||||
|
size: 22,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget morePanel(BuildContext context, BangumiIntroController ctr) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||||
|
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))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...ctr.followStatusList
|
||||||
|
.map(
|
||||||
|
(e) => ListTile(
|
||||||
|
onTap: () => ctr.updateBangumiStatus(e['status']),
|
||||||
|
selected: ctr.followStatus == e['status'],
|
||||||
|
title: Text(e['title']),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -76,9 +76,14 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Obx(
|
||||||
'最近追番',
|
() => 0 != _bangumidController.total.value
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
? Text(
|
||||||
|
'我的追番(${_bangumidController.total.value})',
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleMedium,
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|||||||
@ -175,11 +175,13 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
return Container(
|
return Container(
|
||||||
width: 150,
|
width: 150,
|
||||||
margin: const EdgeInsets.only(right: 10),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
child: Material(
|
clipBehavior: Clip.antiAlias,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(8),
|
||||||
clipBehavior: Clip.hardEdge,
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
onTap: () => changeFucCall(page, i),
|
onTap: () => changeFucCall(page, i),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@ -230,7 +232,6 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -25,6 +25,7 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
RoutePush.bangumiPush(
|
RoutePush.bangumiPush(
|
||||||
bangumiItem.seasonId,
|
bangumiItem.seasonId,
|
||||||
null,
|
null,
|
||||||
|
progressIndex: bangumiItem.progressIndex,
|
||||||
heroTag: heroTag,
|
heroTag: heroTag,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -87,7 +87,9 @@ class _BlackListPageState extends State<BlackListPage> {
|
|||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {},
|
onTap: () => Get.toNamed(
|
||||||
|
'/member?mid=${list[index].mid}',
|
||||||
|
arguments: {'face': list[index].face}),
|
||||||
leading: NetworkImgLayer(
|
leading: NetworkImgLayer(
|
||||||
width: 45,
|
width: 45,
|
||||||
height: 45,
|
height: 45,
|
||||||
|
|||||||
@ -96,7 +96,9 @@ class VideoContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
[22, 0].contains(favFolderItem.attr) ? '公开' : '私密',
|
Constants.publicFavFolder.contains(favFolderItem.attr)
|
||||||
|
? '公开'
|
||||||
|
: '私密',
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class LiveController extends GetxController {
|
|||||||
RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
|
RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
|
||||||
RxList<LiveFollowingItemModel> liveFollowingList =
|
RxList<LiveFollowingItemModel> liveFollowingList =
|
||||||
<LiveFollowingItemModel>[].obs;
|
<LiveFollowingItemModel>[].obs;
|
||||||
|
RxInt liveFollowingCount = 0.obs;
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
OverlayEntry? popupDialog;
|
OverlayEntry? popupDialog;
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
@ -27,9 +28,6 @@ class LiveController extends GetxController {
|
|||||||
|
|
||||||
// 获取推荐
|
// 获取推荐
|
||||||
Future queryLiveList(type) async {
|
Future queryLiveList(type) async {
|
||||||
// if (type == 'init') {
|
|
||||||
// _currentPage = 1;
|
|
||||||
// }
|
|
||||||
var res = await LiveHttp.liveList(
|
var res = await LiveHttp.liveList(
|
||||||
pn: _currentPage,
|
pn: _currentPage,
|
||||||
);
|
);
|
||||||
@ -68,13 +66,14 @@ class LiveController extends GetxController {
|
|||||||
|
|
||||||
//
|
//
|
||||||
Future fetchLiveFollowing() async {
|
Future fetchLiveFollowing() async {
|
||||||
var res = await LiveHttp.liveFollowing(pn: 1, ps: 20);
|
var res = await LiveHttp.liveFollowing(pn: 1, ps: 10);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
liveFollowingList.value =
|
liveFollowingList.value =
|
||||||
(res['data'].list as List<LiveFollowingItemModel>)
|
(res['data'].list as List<LiveFollowingItemModel>)
|
||||||
.where((LiveFollowingItemModel item) =>
|
.where((LiveFollowingItemModel item) =>
|
||||||
item.liveStatus == 1 && item.recordLiveTime == 0) // 根据条件过滤
|
item.liveStatus == 1 && item.recordLiveTime == 0) // 根据条件过滤
|
||||||
.toList();
|
.toList();
|
||||||
|
liveFollowingCount.value = res['data'].liveCount;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -161,6 +161,9 @@ class _LivePageState extends State<LivePage>
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
() => Text.rich(
|
() => Text.rich(
|
||||||
@ -174,7 +177,7 @@ class _LivePageState extends State<LivePage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' ${_liveController.liveFollowingList.length}',
|
text: ' ${_liveController.liveFollowingCount}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
@ -191,6 +194,30 @@ class _LivePageState extends State<LivePage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed('/liveFollowing');
|
||||||
|
},
|
||||||
|
highlightColor: Colors.transparent,
|
||||||
|
splashColor: Colors.transparent,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'查看更多',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.chevron_right,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture2,
|
future: _futureBuilderFuture2,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -201,8 +228,7 @@ class _LivePageState extends State<LivePage>
|
|||||||
Map? data = snapshot.data;
|
Map? data = snapshot.data;
|
||||||
if (data?['status']) {
|
if (data?['status']) {
|
||||||
RxList list = _liveController.liveFollowingList;
|
RxList list = _liveController.liveFollowingList;
|
||||||
// ignore: invalid_use_of_protected_member
|
return LiveFollowingListView(list: list);
|
||||||
return Obx(() => LiveFollowingListView(list: list.value));
|
|
||||||
} else {
|
} else {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 80,
|
height: 80,
|
||||||
@ -230,13 +256,14 @@ class _LivePageState extends State<LivePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LiveFollowingListView extends StatelessWidget {
|
class LiveFollowingListView extends StatelessWidget {
|
||||||
final List list;
|
final RxList list;
|
||||||
|
|
||||||
const LiveFollowingListView({super.key, required this.list});
|
const LiveFollowingListView({super.key, required this.list});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return Obx(
|
||||||
|
() => SizedBox(
|
||||||
height: 100,
|
height: 100,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
@ -294,6 +321,7 @@ class LiveFollowingListView extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@ 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/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
|
import 'package:pilipala/models/live/follow.dart';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
import 'package:pilipala/utils/image_save.dart';
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
@ -9,7 +11,7 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
|
|
||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class LiveCardV extends StatelessWidget {
|
class LiveCardV extends StatelessWidget {
|
||||||
final LiveItemModel liveItem;
|
final dynamic liveItem;
|
||||||
final int crossAxisCount;
|
final int crossAxisCount;
|
||||||
|
|
||||||
const LiveCardV({
|
const LiveCardV({
|
||||||
@ -64,6 +66,9 @@ class LiveCardV extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (liveItem is LiveFollowingItemModel &&
|
||||||
|
liveItem.liveStatus == 1)
|
||||||
|
const PBadge(top: 8, right: 8, text: '直播中'),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -148,7 +153,7 @@ class LiveContent extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoStat extends StatelessWidget {
|
class VideoStat extends StatelessWidget {
|
||||||
final LiveItemModel? liveItem;
|
final dynamic liveItem;
|
||||||
|
|
||||||
const VideoStat({
|
const VideoStat({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -178,25 +183,20 @@ class VideoStat extends StatelessWidget {
|
|||||||
liveItem!.areaName!,
|
liveItem!.areaName!,
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
),
|
),
|
||||||
|
if (liveItem is LiveItemModel) ...[
|
||||||
Text(
|
Text(
|
||||||
liveItem!.watchedShow!['text_small'],
|
liveItem!.watchedShow?['text_small'],
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
if (liveItem is LiveFollowingItemModel) ...[
|
||||||
|
Text(
|
||||||
|
'${liveItem.textSmall}',
|
||||||
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// child: RichText(
|
|
||||||
// maxLines: 1,
|
|
||||||
// textAlign: TextAlign.justify,
|
|
||||||
// softWrap: false,
|
|
||||||
// text: TextSpan(
|
|
||||||
// style: const TextStyle(fontSize: 11, color: Colors.white),
|
|
||||||
// children: [
|
|
||||||
// TextSpan(text: liveItem!.areaName!),
|
|
||||||
// TextSpan(text: liveItem!.watchedShow!['text_small']),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
lib/pages/live_follow/controller.dart
Normal file
50
lib/pages/live_follow/controller.dart
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/http/live.dart';
|
||||||
|
import 'package:pilipala/models/live/follow.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
class LiveFollowController extends GetxController {
|
||||||
|
RxInt crossAxisCount = 2.obs;
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
|
int _currentPage = 1;
|
||||||
|
RxInt liveFollowingCount = 0.obs;
|
||||||
|
RxList<LiveFollowingItemModel> liveFollowingList =
|
||||||
|
<LiveFollowingItemModel>[].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
crossAxisCount.value =
|
||||||
|
setting.get(SettingBoxKey.customRows, defaultValue: 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future queryLiveFollowList(type) async {
|
||||||
|
var res = await LiveHttp.liveFollowing(
|
||||||
|
pn: _currentPage,
|
||||||
|
ps: 20,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
if (type == 'init') {
|
||||||
|
liveFollowingList.value = res['data'].list;
|
||||||
|
liveFollowingCount.value = res['data'].liveCount;
|
||||||
|
} else if (type == 'onLoad') {
|
||||||
|
liveFollowingList.addAll(res['data'].list);
|
||||||
|
}
|
||||||
|
_currentPage += 1;
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future onRefresh() async {
|
||||||
|
_currentPage = 1;
|
||||||
|
await queryLiveFollowList('init');
|
||||||
|
}
|
||||||
|
|
||||||
|
void onLoad() async {
|
||||||
|
queryLiveFollowList('onLoad');
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/pages/live_follow/index.dart
Normal file
4
lib/pages/live_follow/index.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
library live_follow;
|
||||||
|
|
||||||
|
export 'view.dart';
|
||||||
|
export 'controller.dart';
|
||||||
136
lib/pages/live_follow/view.dart
Normal file
136
lib/pages/live_follow/view.dart
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/constants.dart';
|
||||||
|
import 'package:pilipala/common/skeleton/video_card_v.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/pages/live/widgets/live_item.dart';
|
||||||
|
|
||||||
|
import 'controller.dart';
|
||||||
|
|
||||||
|
class LiveFollowPage extends StatefulWidget {
|
||||||
|
const LiveFollowPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LiveFollowPage> createState() => _LiveFollowPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveFollowPageState extends State<LiveFollowPage> {
|
||||||
|
late Future _futureBuilderFuture;
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
final LiveFollowController _liveFollowController =
|
||||||
|
Get.put(LiveFollowController());
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_futureBuilderFuture = _liveFollowController.queryLiveFollowList('init');
|
||||||
|
scrollController.addListener(
|
||||||
|
() {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
EasyThrottle.throttle(
|
||||||
|
'liveFollowList', const Duration(milliseconds: 200), () {
|
||||||
|
_liveFollowController.onLoad();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
titleSpacing: 0,
|
||||||
|
centerTitle: false,
|
||||||
|
title: Obx(() => Text(
|
||||||
|
'${_liveFollowController.liveFollowingCount}人正在直播中',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
body: Container(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
left: StyleString.safeSpace, right: StyleString.safeSpace),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(StyleString.imgRadius),
|
||||||
|
),
|
||||||
|
child: RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
return await _liveFollowController.onRefresh();
|
||||||
|
},
|
||||||
|
child: CustomScrollView(
|
||||||
|
controller: scrollController,
|
||||||
|
slivers: [
|
||||||
|
SliverPadding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0),
|
||||||
|
sliver: FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
if (snapshot.data == null) {
|
||||||
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
|
}
|
||||||
|
Map data = snapshot.data as Map;
|
||||||
|
if (data['status']) {
|
||||||
|
return SliverLayoutBuilder(
|
||||||
|
builder: (context, boxConstraints) {
|
||||||
|
return Obx(
|
||||||
|
() => contentGrid(_liveFollowController,
|
||||||
|
_liveFollowController.liveFollowingList),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture = _liveFollowController
|
||||||
|
.queryLiveFollowList('init');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return contentGrid(_liveFollowController, []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget contentGrid(ctr, liveList) {
|
||||||
|
int crossAxisCount = ctr.crossAxisCount.value;
|
||||||
|
return SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
|
mainAxisExtent:
|
||||||
|
Get.size.width / crossAxisCount / StyleString.aspectRatio +
|
||||||
|
MediaQuery.textScalerOf(context).scale(
|
||||||
|
(crossAxisCount == 1 ? 48 : 68),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return liveList!.isNotEmpty
|
||||||
|
? LiveCardV(
|
||||||
|
liveItem: liveList[index],
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
|
)
|
||||||
|
: const VideoCardVSkeleton();
|
||||||
|
},
|
||||||
|
childCount: liveList!.isNotEmpty ? liveList!.length : 10,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -122,18 +122,13 @@ class MemberController extends GetxController {
|
|||||||
|
|
||||||
// 合并关注/取关和拉黑逻辑
|
// 合并关注/取关和拉黑逻辑
|
||||||
Future modifyRelation(String actionType) async {
|
Future modifyRelation(String actionType) async {
|
||||||
if (userInfo == null) {
|
|
||||||
SmartDialog.showToast('账号未登录');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String contentText;
|
String contentText;
|
||||||
int act;
|
int act;
|
||||||
if (actionType == 'follow') {
|
if (actionType == 'follow') {
|
||||||
contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?';
|
contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?';
|
||||||
act = memberInfo.value.isFollowed! ? 2 : 1;
|
act = memberInfo.value.isFollowed! ? 2 : 1;
|
||||||
} else if (actionType == 'block') {
|
} else if (actionType == 'block') {
|
||||||
contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主?';
|
contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主?';
|
||||||
act = attribute.value != 128 ? 5 : 6;
|
act = attribute.value != 128 ? 5 : 6;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
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';
|
||||||
@ -7,6 +8,7 @@ import 'package:pilipala/common/constants.dart';
|
|||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/models/member/info.dart';
|
import 'package:pilipala/models/member/info.dart';
|
||||||
import 'package:pilipala/pages/member/index.dart';
|
import 'package:pilipala/pages/member/index.dart';
|
||||||
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'widgets/commen_widget.dart';
|
import 'widgets/commen_widget.dart';
|
||||||
import 'widgets/conis.dart';
|
import 'widgets/conis.dart';
|
||||||
@ -154,6 +156,25 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
bottom: MediaQuery.of(context).padding.bottom + 20,
|
bottom: MediaQuery.of(context).padding.bottom + 20,
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
|
Obx(() {
|
||||||
|
Rx<MemberInfoModel> memberInfo = _memberController.memberInfo;
|
||||||
|
return memberInfo.value.silence != null &&
|
||||||
|
memberInfo.value.silence! == 1
|
||||||
|
? Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.only(top: 10, bottom: 10),
|
||||||
|
color: Theme.of(context).colorScheme.errorContainer,
|
||||||
|
child: Text(
|
||||||
|
'该账号封禁中',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox();
|
||||||
|
}),
|
||||||
profileWidget(),
|
profileWidget(),
|
||||||
|
|
||||||
/// 动态链接
|
/// 动态链接
|
||||||
@ -318,6 +339,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
Rx<MemberInfoModel> memberInfo = _memberController.memberInfo;
|
Rx<MemberInfoModel> memberInfo = _memberController.memberInfo;
|
||||||
return Obx(
|
return Obx(
|
||||||
() => Column(
|
() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ProfilePanel(ctr: _memberController),
|
ProfilePanel(ctr: _memberController),
|
||||||
@ -376,7 +398,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
.value.vip!.label!['img_label_uri_hans_static'],
|
.value.vip!.label!['img_label_uri_hans_static'],
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (memberInfo.value.official!['title'] != '') ...[
|
if (memberInfo.value.official!['title'] != '') ...[
|
||||||
@ -393,6 +415,39 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
feedBack();
|
||||||
|
Clipboard.setData(ClipboardData(
|
||||||
|
text: memberInfo.value.mid.toString()));
|
||||||
|
SmartDialog.showToast('uid复制成功');
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: Ink(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10, vertical: 4),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 16,
|
||||||
|
child: Text(
|
||||||
|
'uid: ${memberInfo.value.mid}',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSurfaceVariant,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
SelectableText(memberInfo.value.sign ?? ''),
|
SelectableText(memberInfo.value.sign ?? ''),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -100,13 +100,6 @@ class SystemItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// if (item.content is Map) {
|
|
||||||
// var res = MessageUtils().extractLinks(item.content['web']);
|
|
||||||
// print('res: $res');
|
|
||||||
// } else {
|
|
||||||
// var res = MessageUtils().extractLinks(item.content);
|
|
||||||
// print('res: $res');
|
|
||||||
// }
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(14, 14, 14, 12),
|
padding: const EdgeInsets.fromLTRB(14, 14, 14, 12),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -156,7 +149,7 @@ class SystemItem extends StatelessWidget {
|
|||||||
contentMap['message'].splitMapJoin(
|
contentMap['message'].splitMapJoin(
|
||||||
regExp,
|
regExp,
|
||||||
onMatch: (Match match) {
|
onMatch: (Match match) {
|
||||||
if (!match.group(0)!.startsWith('BV')) {
|
if (match.group(0) != '' && !match.group(0)!.startsWith('BV')) {
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Icon(Icons.link, color: colorScheme.primary, size: 16),
|
child: Icon(Icons.link, color: colorScheme.primary, size: 16),
|
||||||
|
|||||||
@ -26,7 +26,6 @@ class SSearchController extends GetxController {
|
|||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
bool enableHotKey = true;
|
bool enableHotKey = true;
|
||||||
bool enableSearchSuggest = true;
|
bool enableSearchSuggest = true;
|
||||||
late StreamController<bool> clearStream = StreamController<bool>.broadcast();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -42,7 +41,6 @@ class SSearchController extends GetxController {
|
|||||||
final hint = parameters['hintText'];
|
final hint = parameters['hintText'];
|
||||||
if (hint != null) {
|
if (hint != null) {
|
||||||
hintText = hint;
|
hintText = hint;
|
||||||
searchKeyWord.value = hintText;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
historyCacheList = GlobalDataCache().historyCacheList;
|
historyCacheList = GlobalDataCache().historyCacheList;
|
||||||
@ -55,10 +53,8 @@ class SSearchController extends GetxController {
|
|||||||
searchKeyWord.value = value;
|
searchKeyWord.value = value;
|
||||||
if (value == '') {
|
if (value == '') {
|
||||||
searchSuggestList.value = [];
|
searchSuggestList.value = [];
|
||||||
clearStream.add(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clearStream.add(true);
|
|
||||||
if (enableSearchSuggest) {
|
if (enableSearchSuggest) {
|
||||||
_debouncer.call(() => querySearchSuggest(value));
|
_debouncer.call(() => querySearchSuggest(value));
|
||||||
}
|
}
|
||||||
@ -68,24 +64,20 @@ class SSearchController extends GetxController {
|
|||||||
controller.value.clear();
|
controller.value.clear();
|
||||||
searchKeyWord.value = '';
|
searchKeyWord.value = '';
|
||||||
searchSuggestList.value = [];
|
searchSuggestList.value = [];
|
||||||
clearStream.add(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
void submit() {
|
void submit() {
|
||||||
if (searchKeyWord.value == '') {
|
if (searchKeyWord.value == '' && hintText.isNotEmpty && hintText == '搜索') {
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
if (searchKeyWord.value == '' && hintText != '搜索') {
|
||||||
|
searchKeyWord.value = hintText;
|
||||||
|
controller.value.text = hintText;
|
||||||
}
|
}
|
||||||
List arr = historyCacheList.where((e) => e != searchKeyWord.value).toList();
|
}
|
||||||
arr.insert(0, searchKeyWord.value);
|
hintText = '搜索';
|
||||||
historyCacheList = arr;
|
cacheHistory();
|
||||||
|
|
||||||
historyList.value = historyCacheList;
|
|
||||||
// 手动刷新
|
|
||||||
historyList.refresh();
|
|
||||||
localCache.put('cacheList', historyCacheList);
|
|
||||||
GlobalDataCache().historyCacheList = historyCacheList;
|
|
||||||
searchFocusNode.unfocus();
|
|
||||||
Get.toNamed('/searchResult', parameters: {'keyword': searchKeyWord.value});
|
Get.toNamed('/searchResult', parameters: {'keyword': searchKeyWord.value});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,4 +131,15 @@ class SSearchController extends GetxController {
|
|||||||
GlobalDataCache().historyCacheList = [];
|
GlobalDataCache().historyCacheList = [];
|
||||||
SmartDialog.showToast('搜索历史已清空');
|
SmartDialog.showToast('搜索历史已清空');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheHistory() {
|
||||||
|
List arr = historyCacheList.where((e) => e != searchKeyWord.value).toList();
|
||||||
|
arr.insert(0, searchKeyWord.value);
|
||||||
|
historyCacheList = arr;
|
||||||
|
historyList.value = historyCacheList;
|
||||||
|
historyList.refresh();
|
||||||
|
localCache.put('cacheList', historyCacheList);
|
||||||
|
GlobalDataCache().historyCacheList = historyCacheList;
|
||||||
|
searchFocusNode.unfocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,24 +63,35 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
focusNode: _searchController.searchFocusNode,
|
focusNode: _searchController.searchFocusNode,
|
||||||
controller: _searchController.controller.value,
|
controller: _searchController.controller.value,
|
||||||
textInputAction: TextInputAction.search,
|
textInputAction: TextInputAction.search,
|
||||||
onChanged: (value) => _searchController.onChange(value),
|
onChanged: _searchController.onChange,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: _searchController.hintText,
|
hintText: _searchController.hintText,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
suffixIcon: StreamBuilder(
|
suffix: Obx(() {
|
||||||
initialData: false,
|
RxString searchKeyWord = _searchController.searchKeyWord;
|
||||||
stream: _searchController.clearStream.stream,
|
if (searchKeyWord.value.isEmpty) {
|
||||||
builder: (_, snapshot) {
|
|
||||||
if (snapshot.data == true) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.clear, size: 22),
|
|
||||||
onPressed: () => _searchController.onClear(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (RegExp(r'^\d+$').hasMatch(searchKeyWord.value))
|
||||||
|
IconButton(
|
||||||
|
tooltip: '直达up主页',
|
||||||
|
icon: const Icon(Icons.person_outline, size: 22),
|
||||||
|
onPressed: () {
|
||||||
|
_searchController.cacheHistory();
|
||||||
|
Get.toNamed('/member?mid=${searchKeyWord.value}',
|
||||||
|
arguments: {'face': null});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear, size: 22),
|
||||||
|
onPressed: () => _searchController.onClear(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
onSubmitted: (String value) => _searchController.submit(),
|
onSubmitted: (String value) => _searchController.submit(),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/member.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
|
import 'package:pilipala/models/search/result.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ class SearchPanelController extends GetxController {
|
|||||||
tids: searchType!.type != 'video' ? null : tids.value,
|
tids: searchType!.type != 'video' ? null : tids.value,
|
||||||
);
|
);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
if (type == 'onRefresh') {
|
if (type == 'init') {
|
||||||
resultList.value = result['data'].list ?? [];
|
resultList.value = result['data'].list ?? [];
|
||||||
} else {
|
} else {
|
||||||
resultList.addAll(result['data'].list ?? []);
|
resultList.addAll(result['data'].list ?? []);
|
||||||
@ -39,12 +41,36 @@ class SearchPanelController extends GetxController {
|
|||||||
page.value++;
|
page.value++;
|
||||||
onPushDetail(keyword, resultList);
|
onPushDetail(keyword, resultList);
|
||||||
}
|
}
|
||||||
|
if (RegExp(r'^\d+$').hasMatch(keyword!) &&
|
||||||
|
searchType == SearchType.bili_user) {
|
||||||
|
var res = await MemberHttp.memberInfo(mid: int.parse(keyword!));
|
||||||
|
if (res['status']) {
|
||||||
|
try {
|
||||||
|
final user = SearchUserItemModel(
|
||||||
|
mid: res['data'].mid,
|
||||||
|
uname: res['data'].name,
|
||||||
|
upic: res['data'].face,
|
||||||
|
level: res['data'].level,
|
||||||
|
fans: null,
|
||||||
|
videos: null,
|
||||||
|
officialVerify: res['data'].official,
|
||||||
|
);
|
||||||
|
if (resultList.isEmpty) {
|
||||||
|
resultList = [user].obs;
|
||||||
|
} else {
|
||||||
|
resultList.insert(0, user);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
debugPrint('搜索用户信息失败: $err');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future onRefresh() async {
|
Future onRefresh() async {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
await onSearch(type: 'onRefresh');
|
await onSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回顶部并刷新
|
// 返回顶部并刷新
|
||||||
|
|||||||
@ -4,6 +4,7 @@ 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/skeleton/media_bangumi.dart';
|
import 'package:pilipala/common/skeleton/media_bangumi.dart';
|
||||||
|
import 'package:pilipala/common/skeleton/user_list.dart';
|
||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
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/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
@ -81,11 +82,11 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data != null) {
|
Map? data = snapshot.data;
|
||||||
Map data = snapshot.data;
|
if (data != null && data['status']) {
|
||||||
var ctr = _searchPanelController;
|
var ctr = _searchPanelController;
|
||||||
RxList list = ctr.resultList;
|
RxList list = ctr.resultList;
|
||||||
if (data['status']) {
|
if (list.isNotEmpty) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
switch (widget.searchType) {
|
switch (widget.searchType) {
|
||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
@ -110,21 +111,18 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: '没有数据',
|
||||||
fn: () {
|
isShowBtn: false,
|
||||||
setState(() {
|
fn: () => {},
|
||||||
_searchPanelController.onSearch();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isInSliver: false,
|
isInSliver: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: '没有相关数据',
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
fn: () {
|
fn: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_searchPanelController.onSearch();
|
_futureBuilderFuture = _searchPanelController.onRefresh();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isInSliver: false,
|
isInSliver: false,
|
||||||
@ -143,7 +141,7 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
case SearchType.media_bangumi:
|
case SearchType.media_bangumi:
|
||||||
return const MediaBangumiSkeleton();
|
return const MediaBangumiSkeleton();
|
||||||
case SearchType.bili_user:
|
case SearchType.bili_user:
|
||||||
return const VideoCardHSkeleton();
|
return const UserListSkeleton();
|
||||||
case SearchType.live_room:
|
case SearchType.live_room:
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -1,12 +1,7 @@
|
|||||||
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:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.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/search.dart';
|
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
|
||||||
import 'package:pilipala/utils/route_push.dart';
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -30,8 +25,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
// });
|
// });
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.symmetric(
|
||||||
StyleString.safeSpace, 7, StyleString.safeSpace, 7),
|
horizontal: StyleString.safeSpace, vertical: 7),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -12,15 +13,16 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
|
|||||||
controller: ctr!.scrollController,
|
controller: ctr!.scrollController,
|
||||||
addAutomaticKeepAlives: false,
|
addAutomaticKeepAlives: false,
|
||||||
addRepaintBoundaries: false,
|
addRepaintBoundaries: false,
|
||||||
itemCount: list!.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var i = list![index];
|
var i = list[index];
|
||||||
String heroTag = Utils.makeHeroTag(i!.mid);
|
String heroTag = Utils.makeHeroTag(i.mid);
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => Get.toNamed('/member?mid=${i.mid}',
|
onTap: () => Get.toNamed('/member?mid=${i.mid}',
|
||||||
arguments: {'heroTag': heroTag, 'face': i.upic}),
|
arguments: {'heroTag': heroTag, 'face': i.upic}),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: StyleString.safeSpace, vertical: 7),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
@ -41,7 +43,7 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i!.uname,
|
i.uname!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
@ -53,15 +55,16 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (i.fans != null && i.videos != null)
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text('粉丝:${i.fans} ', style: style),
|
Text('粉丝:${i.fans} ', style: style),
|
||||||
Text(' 视频:${i.videos}', style: style)
|
Text(' 视频:${i.videos}', style: style)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (i.officialVerify['desc'] != '')
|
if (i.officialVerify!['desc'] != '')
|
||||||
Text(
|
Text(
|
||||||
i.officialVerify['desc'],
|
i.officialVerify!['desc'],
|
||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -3,6 +3,7 @@ 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/search_panel/index.dart';
|
import 'package:pilipala/pages/search_panel/index.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
import 'widget/tab_bar.dart';
|
||||||
|
|
||||||
class SearchResultPage extends StatefulWidget {
|
class SearchResultPage extends StatefulWidget {
|
||||||
const SearchResultPage({super.key});
|
const SearchResultPage({super.key});
|
||||||
@ -29,6 +30,17 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tab点击事件
|
||||||
|
void _onTap(int index) {
|
||||||
|
if (index == _searchResultController.tabIndex) {
|
||||||
|
Get.find<SearchPanelController>(
|
||||||
|
tag: SearchType.values[index].type +
|
||||||
|
_searchResultController.keyword!)
|
||||||
|
.animateToTop();
|
||||||
|
}
|
||||||
|
_searchResultController.tabIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -55,50 +67,10 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Container(
|
TabBarWidget(
|
||||||
width: double.infinity,
|
onTap: _onTap,
|
||||||
padding: const EdgeInsets.only(left: 8),
|
tabController: _tabController!,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
searchResultCtr: _searchResultController,
|
||||||
child: Theme(
|
|
||||||
data: ThemeData(
|
|
||||||
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
|
|
||||||
highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
|
||||||
),
|
|
||||||
child: Obx(
|
|
||||||
() => (TabBar(
|
|
||||||
controller: _tabController,
|
|
||||||
tabs: [
|
|
||||||
for (var i in _searchResultController.searchTabs)
|
|
||||||
Tab(text: "${i['label']} ${i['count'] ?? ''}")
|
|
||||||
],
|
|
||||||
isScrollable: true,
|
|
||||||
indicatorWeight: 0,
|
|
||||||
indicatorPadding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
|
|
||||||
indicator: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
|
||||||
),
|
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
|
||||||
labelColor:
|
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
|
||||||
labelStyle: const TextStyle(fontSize: 13),
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
unselectedLabelColor: Theme.of(context).colorScheme.outline,
|
|
||||||
tabAlignment: TabAlignment.start,
|
|
||||||
onTap: (index) {
|
|
||||||
if (index == _searchResultController.tabIndex) {
|
|
||||||
Get.find<SearchPanelController>(
|
|
||||||
tag: SearchType.values[index].type +
|
|
||||||
_searchResultController.keyword!)
|
|
||||||
.animateToTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
_searchResultController.tabIndex = index;
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
|
|||||||
53
lib/pages/search_result/widget/tab_bar.dart
Normal file
53
lib/pages/search_result/widget/tab_bar.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/pages/search_result/index.dart';
|
||||||
|
|
||||||
|
class TabBarWidget extends StatelessWidget {
|
||||||
|
final Function(int) onTap;
|
||||||
|
final TabController tabController;
|
||||||
|
final SearchResultController searchResultCtr;
|
||||||
|
|
||||||
|
const TabBarWidget({
|
||||||
|
required this.onTap,
|
||||||
|
required this.tabController,
|
||||||
|
required this.searchResultCtr,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
ColorScheme colorScheme = Theme.of(context).colorScheme;
|
||||||
|
Color transparent = Colors.transparent;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
color: colorScheme.surface,
|
||||||
|
child: Theme(
|
||||||
|
data: ThemeData(splashColor: transparent, highlightColor: transparent),
|
||||||
|
child: Obx(
|
||||||
|
() => TabBar(
|
||||||
|
controller: tabController,
|
||||||
|
tabs: [
|
||||||
|
for (var i in searchResultCtr.searchTabs)
|
||||||
|
Tab(text: "${i['label']} ${i['count'] ?? ''}"),
|
||||||
|
],
|
||||||
|
isScrollable: true,
|
||||||
|
indicatorPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
|
||||||
|
indicator: BoxDecoration(
|
||||||
|
color: colorScheme.secondaryContainer,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
|
labelColor: colorScheme.onSecondaryContainer,
|
||||||
|
labelStyle: const TextStyle(fontSize: 13),
|
||||||
|
dividerColor: transparent,
|
||||||
|
unselectedLabelColor: colorScheme.outline,
|
||||||
|
tabAlignment: TabAlignment.start,
|
||||||
|
onTap: onTap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -90,12 +90,15 @@ class VideoContent extends StatelessWidget {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
|
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
|
||||||
child: Column(
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
subFolderItem.title!,
|
subFolderItem.title!,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
|
maxLines: 3,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0.3,
|
letterSpacing: 0.3,
|
||||||
@ -119,12 +122,13 @@ class VideoContent extends StatelessWidget {
|
|||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
],
|
||||||
|
),
|
||||||
isOwner
|
isOwner
|
||||||
? Row(
|
? Positioned(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
right: 0,
|
||||||
children: [
|
bottom: -4,
|
||||||
IconButton(
|
child: IconButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
@ -134,8 +138,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
size: 18,
|
size: 18,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
|
||||||
)
|
)
|
||||||
: const SizedBox()
|
: const SizedBox()
|
||||||
],
|
],
|
||||||
|
|||||||
@ -338,57 +338,79 @@ class VideoIntroController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int currentStatus = followStatus['attribute'];
|
final int currentStatus = followStatus['attribute'];
|
||||||
int actionStatus = 0;
|
if (currentStatus == 128) {
|
||||||
switch (currentStatus) {
|
modifyRelation('block', currentStatus);
|
||||||
case 0:
|
} else {
|
||||||
actionStatus = 1;
|
modifyRelation('follow', currentStatus);
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
actionStatus = 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
actionStatus = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
SmartDialog.show(
|
}
|
||||||
useSystem: true,
|
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
// 操作用户关系
|
||||||
|
Future modifyRelation(String actionType, int currentStatus) async {
|
||||||
|
final int mid = videoDetail.value.owner!.mid!;
|
||||||
|
String contentText;
|
||||||
|
int act;
|
||||||
|
if (actionType == 'follow') {
|
||||||
|
contentText = currentStatus != 0 ? '确定取消关注UP主?' : '确定关注UP主?';
|
||||||
|
act = currentStatus != 0 ? 2 : 1;
|
||||||
|
} else if (actionType == 'block') {
|
||||||
|
contentText = '确定从黑名单移除UP主?';
|
||||||
|
act = 6;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: Get.context!,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
|
final Color outline = Theme.of(Get.context!).colorScheme.outline;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(currentStatus == 0 ? '关注UP主?' : '取消关注UP主?'),
|
content: Text(contentText),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: Navigator.of(context).pop,
|
||||||
child: Text(
|
child: Text('点错了', style: TextStyle(color: outline)),
|
||||||
'点错了',
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () => modifyRelationFetch(
|
||||||
var result = await VideoHttp.relationMod(
|
context,
|
||||||
mid: videoDetail.value.owner!.mid!,
|
mid,
|
||||||
act: actionStatus,
|
act,
|
||||||
reSrc: 14,
|
currentStatus,
|
||||||
|
actionType,
|
||||||
|
),
|
||||||
|
child: const Text('确定'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
if (result['status']) {
|
|
||||||
switch (currentStatus) {
|
|
||||||
case 0:
|
|
||||||
actionStatus = 2;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
actionStatus = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
actionStatus = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
followStatus['attribute'] = actionStatus;
|
|
||||||
followStatus.refresh();
|
// 操作用户关系Future
|
||||||
if (actionStatus == 2) {
|
Future modifyRelationFetch(
|
||||||
|
BuildContext context,
|
||||||
|
mid,
|
||||||
|
act,
|
||||||
|
currentStatus,
|
||||||
|
actionType,
|
||||||
|
) async {
|
||||||
|
var res = await VideoHttp.relationMod(mid: mid, act: act, reSrc: 11);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
if (res['status']) {
|
||||||
|
if (actionType == 'follow') {
|
||||||
|
final Map<int, int> statusMap = {
|
||||||
|
0: 2,
|
||||||
|
2: 0,
|
||||||
|
};
|
||||||
|
late int actionStatus;
|
||||||
|
actionStatus = statusMap[currentStatus] ?? 0;
|
||||||
|
followStatus['attribute'] = actionStatus;
|
||||||
|
if (currentStatus == 0 && Get.context!.mounted) {
|
||||||
|
ScaffoldMessenger.of(Get.context!).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: const Text('关注成功'),
|
content: const Text('关注成功'),
|
||||||
duration: const Duration(seconds: 2),
|
duration: const Duration(seconds: 2),
|
||||||
@ -399,17 +421,17 @@ class VideoIntroController extends GetxController {
|
|||||||
showCloseIcon: true,
|
showCloseIcon: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('取消关注成功');
|
||||||
}
|
}
|
||||||
|
} else if (actionType == 'block') {
|
||||||
|
followStatus['attribute'] = 0;
|
||||||
|
SmartDialog.showToast('取消拉黑成功');
|
||||||
}
|
}
|
||||||
|
followStatus.refresh();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
|
||||||
},
|
|
||||||
child: const Text('确认'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改分P或番剧分集
|
// 修改分P或番剧分集
|
||||||
|
|||||||
@ -470,8 +470,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
final bool isFollowed =
|
final int attr =
|
||||||
videoIntroController.followStatus['attribute'] != 0;
|
videoIntroController.followStatus['attribute'] ?? 0;
|
||||||
return videoIntroController.followStatus.isEmpty
|
return videoIntroController.followStatus.isEmpty
|
||||||
? const SizedBox()
|
? const SizedBox()
|
||||||
: SizedBox(
|
: SizedBox(
|
||||||
@ -484,15 +484,19 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
left: 8,
|
left: 8,
|
||||||
right: 8,
|
right: 8,
|
||||||
),
|
),
|
||||||
foregroundColor: isFollowed
|
foregroundColor: attr != 0
|
||||||
? outline
|
? outline
|
||||||
: t.colorScheme.onPrimary,
|
: t.colorScheme.onPrimary,
|
||||||
backgroundColor: isFollowed
|
backgroundColor: attr != 0
|
||||||
? t.colorScheme.onInverseSurface
|
? t.colorScheme.onInverseSurface
|
||||||
: t.colorScheme.primary, // 设置按钮背景色
|
: t.colorScheme.primary, // 设置按钮背景色
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
isFollowed ? '已关注' : '关注',
|
attr == 128
|
||||||
|
? '已拉黑'
|
||||||
|
: attr != 0
|
||||||
|
? '已关注'
|
||||||
|
: '关注',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize:
|
fontSize:
|
||||||
t.textTheme.labelMedium!.fontSize,
|
t.textTheme.labelMedium!.fontSize,
|
||||||
|
|||||||
@ -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:hive/hive.dart';
|
import 'package:hive/hive.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/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
@ -66,16 +67,14 @@ class _FavPanelState extends State<FavPanel> {
|
|||||||
onTap: () =>
|
onTap: () =>
|
||||||
widget.ctr!.onChoose(item.favState != 1, index),
|
widget.ctr!.onChoose(item.favState != 1, index),
|
||||||
dense: true,
|
dense: true,
|
||||||
leading: Icon([22, 0].contains(item.attr)
|
leading: Icon(
|
||||||
? Icons.lock_outline
|
Constants.publicFavFolder.contains(item.attr)
|
||||||
: Icons.folder_outlined),
|
? Icons.folder_outlined
|
||||||
|
: Icons.lock_outline),
|
||||||
minLeadingWidth: 0,
|
minLeadingWidth: 0,
|
||||||
title: Text(item.title!),
|
title: Text(item.title!),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'${item.mediaCount}个内容 - ${[
|
'${item.mediaCount}个内容 - ${Constants.publicFavFolder.contains(item.attr) ? '公开' : '私密'}',
|
||||||
22,
|
|
||||||
0
|
|
||||||
].contains(item.attr) ? '公开' : '私密'}',
|
|
||||||
),
|
),
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.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/constants.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';
|
||||||
|
|
||||||
@ -15,6 +16,10 @@ class IntroDetail extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
TextStyle textStyle = TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -29,12 +34,7 @@ class IntroDetail extends StatelessWidget {
|
|||||||
Clipboard.setData(ClipboardData(text: videoDetail!.bvid!));
|
Clipboard.setData(ClipboardData(text: videoDetail!.bvid!));
|
||||||
SmartDialog.showToast('已复制');
|
SmartDialog.showToast('已复制');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(videoDetail!.bvid!, style: textStyle),
|
||||||
videoDetail!.bvid!,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: Theme.of(context).colorScheme.primary),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
@ -44,12 +44,18 @@ class IntroDetail extends StatelessWidget {
|
|||||||
ClipboardData(text: videoDetail!.aid!.toString()));
|
ClipboardData(text: videoDetail!.aid!.toString()));
|
||||||
SmartDialog.showToast('已复制');
|
SmartDialog.showToast('已复制');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(videoDetail!.aid!.toString(), style: textStyle),
|
||||||
videoDetail!.aid!.toString(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: Theme.of(context).colorScheme.primary),
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
feedBack();
|
||||||
|
String videoUrl =
|
||||||
|
'${HttpString.baseUrl}/video/${videoDetail!.bvid!}';
|
||||||
|
Clipboard.setData(ClipboardData(text: videoUrl));
|
||||||
|
SmartDialog.showToast('已复制视频链接');
|
||||||
|
},
|
||||||
|
child: Text('复制链接', style: textStyle),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
final bool? showReplyRow;
|
final bool? showReplyRow;
|
||||||
final Function? replyReply;
|
final Function? replyReply;
|
||||||
final ReplyType? replyType;
|
final ReplyType? replyType;
|
||||||
final bool? replySave;
|
final bool replySave;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -55,14 +55,14 @@ class ReplyItem extends StatelessWidget {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
// 点击整个评论区 评论详情/回复
|
// 点击整个评论区 评论详情/回复
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (replySave!) {
|
if (replySave) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
feedBack();
|
feedBack();
|
||||||
replyReply?.call(replyItem, null, replyItem!.rcount! > 0);
|
replyReply?.call(replyItem, null, replyItem!.rcount! > 0);
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (replySave!) {
|
if (replySave) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
feedBack();
|
feedBack();
|
||||||
@ -235,14 +235,17 @@ class ReplyItem extends StatelessWidget {
|
|||||||
// title
|
// title
|
||||||
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: LayoutBuilder(
|
child: !replySave
|
||||||
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
? LayoutBuilder(builder:
|
||||||
|
(BuildContext context, BoxConstraints boxConstraints) {
|
||||||
String text = replyItem?.content?.message ?? '';
|
String text = replyItem?.content?.message ?? '';
|
||||||
bool didExceedMaxLines = false;
|
bool didExceedMaxLines = false;
|
||||||
final double maxWidth = boxConstraints.maxWidth;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
TextPainter? textPainter;
|
TextPainter? textPainter;
|
||||||
final int maxLines =
|
final int maxLines =
|
||||||
replyItem!.content!.isText! && replyLevel == '1' ? 6 : 999;
|
replyItem!.content!.isText! && replyLevel == '1'
|
||||||
|
? 6
|
||||||
|
: 999;
|
||||||
try {
|
try {
|
||||||
textPainter = TextPainter(
|
textPainter = TextPainter(
|
||||||
text: TextSpan(text: text),
|
text: TextSpan(text: text),
|
||||||
@ -255,6 +258,31 @@ class ReplyItem extends StatelessWidget {
|
|||||||
debugPrint('Error while measuring text: $e');
|
debugPrint('Error while measuring text: $e');
|
||||||
didExceedMaxLines = false;
|
didExceedMaxLines = false;
|
||||||
}
|
}
|
||||||
|
return replyContent(context, didExceedMaxLines, textPainter);
|
||||||
|
})
|
||||||
|
: replyContent(context, false, null),
|
||||||
|
),
|
||||||
|
// 操作区域
|
||||||
|
bottonAction(context, replyItem!.replyControl, replySave),
|
||||||
|
// 一楼的评论
|
||||||
|
if ((replyItem!.rcount! > 0) && showReplyRow!) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 5, bottom: 12),
|
||||||
|
child: ReplyItemRow(
|
||||||
|
replies: replyItem!.replies,
|
||||||
|
replyControl: replyItem!.replyControl,
|
||||||
|
// f_rpid: replyItem!.rpid,
|
||||||
|
replyItem: replyItem,
|
||||||
|
replyReply: replyReply,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget replyContent(
|
||||||
|
BuildContext context, bool? didExceedMaxLines, TextPainter? textPainter) {
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
style: const TextStyle(height: 1.75),
|
style: const TextStyle(height: 1.75),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
@ -275,31 +303,12 @@ class ReplyItem extends StatelessWidget {
|
|||||||
replyItem!,
|
replyItem!,
|
||||||
replyReply,
|
replyReply,
|
||||||
null,
|
null,
|
||||||
didExceedMaxLines,
|
didExceedMaxLines ?? false,
|
||||||
textPainter,
|
textPainter,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
|
||||||
),
|
|
||||||
// 操作区域
|
|
||||||
bottonAction(context, replyItem!.replyControl, replySave),
|
|
||||||
// 一楼的评论
|
|
||||||
if ((replyItem!.rcount! > 0) && showReplyRow!) ...[
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 5, bottom: 12),
|
|
||||||
child: ReplyItemRow(
|
|
||||||
replies: replyItem!.replies,
|
|
||||||
replyControl: replyItem!.replyControl,
|
|
||||||
// f_rpid: replyItem!.rpid,
|
|
||||||
replyItem: replyItem,
|
|
||||||
replyReply: replyReply,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 感谢、回复、复制
|
// 感谢、回复、复制
|
||||||
|
|||||||
@ -153,6 +153,7 @@ class ChatItem extends StatelessWidget {
|
|||||||
jsonDecode(content['content'])
|
jsonDecode(content['content'])
|
||||||
.map((m) => m['text'] as String)
|
.map((m) => m['text'] as String)
|
||||||
.join("\n"),
|
.join("\n"),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
letterSpacing: 0.6,
|
letterSpacing: 0.6,
|
||||||
height: 5,
|
height: 5,
|
||||||
@ -391,7 +392,8 @@ class ChatItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -30,6 +30,13 @@ typedef IndexedFocusedWidgetBuilder = Widget Function(
|
|||||||
|
|
||||||
typedef IndexedTagStringBuilder = String Function(int index);
|
typedef IndexedTagStringBuilder = String Function(int index);
|
||||||
|
|
||||||
|
// 图片操作类型
|
||||||
|
enum ImgActionType {
|
||||||
|
share,
|
||||||
|
copy,
|
||||||
|
save,
|
||||||
|
}
|
||||||
|
|
||||||
class InteractiveviewerGallery<T> extends StatefulWidget {
|
class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||||
const InteractiveviewerGallery({
|
const InteractiveviewerGallery({
|
||||||
required this.sources,
|
required this.sources,
|
||||||
@ -39,6 +46,11 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
|||||||
this.minScale = 1.0,
|
this.minScale = 1.0,
|
||||||
this.onPageChanged,
|
this.onPageChanged,
|
||||||
this.onDismissed,
|
this.onDismissed,
|
||||||
|
this.actionType = const [
|
||||||
|
ImgActionType.share,
|
||||||
|
ImgActionType.copy,
|
||||||
|
ImgActionType.save
|
||||||
|
],
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -59,6 +71,8 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
|||||||
|
|
||||||
final ValueChanged<int>? onDismissed;
|
final ValueChanged<int>? onDismissed;
|
||||||
|
|
||||||
|
final List<ImgActionType> actionType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<InteractiveviewerGallery> createState() =>
|
State<InteractiveviewerGallery> createState() =>
|
||||||
_InteractiveviewerGalleryState();
|
_InteractiveviewerGalleryState();
|
||||||
@ -247,8 +261,8 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
onDoubleTapDown: (TapDownDetails details) {
|
onDoubleTapDown: (TapDownDetails details) {
|
||||||
_doubleTapLocalPosition = details.localPosition;
|
_doubleTapLocalPosition = details.localPosition;
|
||||||
},
|
},
|
||||||
onDoubleTap: onDoubleTap,
|
onDoubleTap: _onDoubleTap,
|
||||||
onLongPress: onLongPress,
|
onLongPress: _onLongPress,
|
||||||
child: widget.itemBuilder != null
|
child: widget.itemBuilder != null
|
||||||
? widget.itemBuilder!(
|
? widget.itemBuilder!(
|
||||||
context,
|
context,
|
||||||
@ -298,28 +312,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
return [
|
return _buildPopupMenuList();
|
||||||
PopupMenuItem(
|
|
||||||
value: 0,
|
|
||||||
onTap: () => onShareImg(widget.sources[currentIndex!]),
|
|
||||||
child: const Text("分享图片"),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: 1,
|
|
||||||
onTap: () {
|
|
||||||
onCopyImg(widget.sources[currentIndex!].toString());
|
|
||||||
},
|
|
||||||
child: const Text("复制图片"),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: 2,
|
|
||||||
onTap: () {
|
|
||||||
DownloadUtils.downloadImg(
|
|
||||||
widget.sources[currentIndex!]);
|
|
||||||
},
|
|
||||||
child: const Text("保存图片"),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.more_horiz, color: Colors.white),
|
child: const Icon(Icons.more_horiz, color: Colors.white),
|
||||||
),
|
),
|
||||||
@ -332,7 +325,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 图片分享
|
// 图片分享
|
||||||
void onShareImg(String imgUrl) async {
|
void _onShareImg(String imgUrl) async {
|
||||||
SmartDialog.showLoading();
|
SmartDialog.showLoading();
|
||||||
var response = await Dio()
|
var response = await Dio()
|
||||||
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
|
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
|
||||||
@ -346,7 +339,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 复制图片
|
// 复制图片
|
||||||
void onCopyImg(String imgUrl) {
|
void _onCopyImg(String imgUrl) {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
ClipboardData(text: widget.sources[currentIndex!].toString()))
|
ClipboardData(text: widget.sources[currentIndex!].toString()))
|
||||||
.then((value) {
|
.then((value) {
|
||||||
@ -380,7 +373,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDoubleTap() {
|
_onDoubleTap() {
|
||||||
Matrix4 matrix = _transformationController!.value.clone();
|
Matrix4 matrix = _transformationController!.value.clone();
|
||||||
double currentScale = matrix.row0.x;
|
double currentScale = matrix.row0.x;
|
||||||
|
|
||||||
@ -427,7 +420,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
.whenComplete(() => _onScaleChanged(targetScale));
|
.whenComplete(() => _onScaleChanged(targetScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
onLongPress() {
|
_onLongPress() {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
@ -456,31 +449,81 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
..._buildListTitles(),
|
||||||
onTap: () {
|
|
||||||
onShareImg(widget.sources[currentIndex!]);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
title: const Text('分享图片'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
onCopyImg(widget.sources[currentIndex!].toString());
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
title: const Text('复制图片'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
DownloadUtils.downloadImg(widget.sources[currentIndex!]);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
title: const Text('保存图片'),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<PopupMenuEntry> _buildPopupMenuList() {
|
||||||
|
List<PopupMenuItem> items = [];
|
||||||
|
for (var i in widget.actionType) {
|
||||||
|
switch (i) {
|
||||||
|
case ImgActionType.share:
|
||||||
|
items.add(PopupMenuItem(
|
||||||
|
value: 0,
|
||||||
|
onTap: () => _onShareImg(widget.sources[currentIndex!]),
|
||||||
|
child: const Text("分享图片"),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case ImgActionType.copy:
|
||||||
|
items.add(PopupMenuItem(
|
||||||
|
value: 1,
|
||||||
|
onTap: () {
|
||||||
|
_onCopyImg(widget.sources[currentIndex!].toString());
|
||||||
|
},
|
||||||
|
child: const Text("复制图片"),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case ImgActionType.save:
|
||||||
|
items.add(PopupMenuItem(
|
||||||
|
value: 2,
|
||||||
|
onTap: () {
|
||||||
|
DownloadUtils.downloadImg(widget.sources[currentIndex!]);
|
||||||
|
},
|
||||||
|
child: const Text("保存图片"),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildListTitles() {
|
||||||
|
List<Widget> items = [];
|
||||||
|
for (var i in widget.actionType) {
|
||||||
|
switch (i) {
|
||||||
|
case ImgActionType.share:
|
||||||
|
items.add(ListTile(
|
||||||
|
onTap: () {
|
||||||
|
_onShareImg(widget.sources[currentIndex!]);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
title: const Text('分享图片'),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case ImgActionType.copy:
|
||||||
|
items.add(ListTile(
|
||||||
|
onTap: () {
|
||||||
|
_onCopyImg(widget.sources[currentIndex!].toString());
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
title: const Text('复制图片'),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case ImgActionType.save:
|
||||||
|
items.add(ListTile(
|
||||||
|
onTap: () {
|
||||||
|
DownloadUtils.downloadImg(widget.sources[currentIndex!]);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
title: const Text('保存图片'),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/pages/fav_edit/index.dart';
|
import 'package:pilipala/pages/fav_edit/index.dart';
|
||||||
import 'package:pilipala/pages/follow_search/view.dart';
|
import 'package:pilipala/pages/follow_search/view.dart';
|
||||||
|
import 'package:pilipala/pages/live_follow/index.dart';
|
||||||
import 'package:pilipala/pages/member_article/index.dart';
|
import 'package:pilipala/pages/member_article/index.dart';
|
||||||
import 'package:pilipala/pages/message/at/index.dart';
|
import 'package:pilipala/pages/message/at/index.dart';
|
||||||
import 'package:pilipala/pages/message/like/index.dart';
|
import 'package:pilipala/pages/message/like/index.dart';
|
||||||
@ -199,6 +200,8 @@ class Routes {
|
|||||||
name: '/memberArticle', page: () => const MemberArticlePage()),
|
name: '/memberArticle', page: () => const MemberArticlePage()),
|
||||||
// 用户信息编辑
|
// 用户信息编辑
|
||||||
CustomGetPage(name: '/mineEdit', page: () => const MineEditPage()),
|
CustomGetPage(name: '/mineEdit', page: () => const MineEditPage()),
|
||||||
|
// 关注的直播up
|
||||||
|
CustomGetPage(name: '/liveFollowing', page: () => const LiveFollowPage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -212,9 +212,9 @@ class PiliSchame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> biliScheme(SchemeEntity value) async {
|
static Future<void> biliScheme(Uri value) async {
|
||||||
final String host = value.host!;
|
final String host = value.host;
|
||||||
final String path = value.path!;
|
final String path = value.path;
|
||||||
switch (host) {
|
switch (host) {
|
||||||
case 'root':
|
case 'root':
|
||||||
Navigator.popUntil(
|
Navigator.popUntil(
|
||||||
@ -301,7 +301,7 @@ class PiliSchame {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
SmartDialog.showToast('未匹配地址,请联系开发者');
|
SmartDialog.showToast('未匹配地址,请联系开发者');
|
||||||
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
|
Clipboard.setData(ClipboardData(text: value.toString()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import 'package:pilipala/utils/utils.dart';
|
|||||||
class RoutePush {
|
class RoutePush {
|
||||||
// 番剧跳转
|
// 番剧跳转
|
||||||
static Future<void> bangumiPush(int? seasonId, int? epId,
|
static Future<void> bangumiPush(int? seasonId, int? epId,
|
||||||
{String? heroTag}) async {
|
{String? heroTag, int? progressIndex}) async {
|
||||||
SmartDialog.showLoading<dynamic>(msg: '获取中...');
|
SmartDialog.showLoading<dynamic>(msg: '获取中...');
|
||||||
try {
|
try {
|
||||||
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId);
|
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId);
|
||||||
@ -19,7 +19,10 @@ class RoutePush {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final BangumiInfoModel bangumiDetail = result['data'];
|
final BangumiInfoModel bangumiDetail = result['data'];
|
||||||
final EpisodeItem episode = bangumiDetail.episodes!.first;
|
EpisodeItem episode = bangumiDetail.episodes!.first;
|
||||||
|
if (progressIndex != null && progressIndex >= 1) {
|
||||||
|
episode = bangumiDetail.episodes![progressIndex - 1];
|
||||||
|
}
|
||||||
final int epId = episode.id!;
|
final int epId = episode.id!;
|
||||||
final int cid = episode.cid!;
|
final int cid = episode.cid!;
|
||||||
final String bvid = episode.bvid!;
|
final String bvid = episode.bvid!;
|
||||||
@ -31,7 +34,7 @@ class RoutePush {
|
|||||||
};
|
};
|
||||||
arguments['heroTag'] = heroTag ?? Utils.makeHeroTag(cid);
|
arguments['heroTag'] = heroTag ?? Utils.makeHeroTag(cid);
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/video?bvid=$bvid&cid=$cid&epId=$epId',
|
'/video?bvid=$bvid&cid=$cid&epId=$epId&seasonId=$seasonId',
|
||||||
arguments: arguments,
|
arguments: arguments,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -218,6 +218,7 @@ flutter:
|
|||||||
- assets/images/logo/
|
- assets/images/logo/
|
||||||
- assets/images/live/
|
- assets/images/live/
|
||||||
- assets/images/video/
|
- assets/images/video/
|
||||||
|
- assets/images/pay/
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user