feat: 视频操作
This commit is contained in:
@ -1,12 +1,72 @@
|
|||||||
class Api {
|
class Api {
|
||||||
// 推荐视频
|
// 推荐视频
|
||||||
static const String recommendList = '/x/web-interface/index/top/feed/rcmd';
|
static const String recommendList = '/x/web-interface/index/top/rcmd';
|
||||||
|
|
||||||
// 热门视频
|
// 热门视频
|
||||||
static const String hotList = '/x/web-interface/popular';
|
static const String hotList = '/x/web-interface/popular';
|
||||||
|
|
||||||
// 视频详情
|
// 视频详情
|
||||||
// 竖屏 https://api.bilibili.com/x/web-interface/view?aid=527403921
|
// 竖屏 https://api.bilibili.com/x/web-interface/view?aid=527403921
|
||||||
// https://api.bilibili.com/x/web-interface/view/detail 获取视频超详细信息(web端)
|
// https://api.bilibili.com/x/web-interface/view/detail 获取视频超详细信息(web端)
|
||||||
static const String videoIntro = '/x/web-interface/view';
|
static const String videoIntro = '/x/web-interface/view';
|
||||||
|
// 视频详情 超详细
|
||||||
|
// https://api.bilibili.com/x/web-interface/view/detail?aid=527403921
|
||||||
|
|
||||||
|
/// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/action.md
|
||||||
|
// 点赞 Post
|
||||||
|
/// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
||||||
|
/// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
||||||
|
/// like num 操作方式 必要 1:点赞 2:取消赞
|
||||||
|
// csrf str CSRF Token(位于cookie) 必要
|
||||||
|
// https://api.bilibili.com/x/web-interface/archive/like
|
||||||
|
static const String likeVideo = '/x/web-interface/archive/like';
|
||||||
|
|
||||||
|
//判断视频是否被点赞(双端)Get
|
||||||
|
// access_key str APP登录Token APP方式必要
|
||||||
|
/// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
||||||
|
/// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
||||||
|
// https://api.bilibili.com/x/web-interface/archive/has/like
|
||||||
|
static const String hasLikeVideo = '/x/web-interface/archive/has/like';
|
||||||
|
|
||||||
|
// 视频点踩 web端不支持
|
||||||
|
|
||||||
|
// 投币视频(web端)POST
|
||||||
|
/// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
||||||
|
/// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
||||||
|
/// multiply num 投币数量 必要 上限为2
|
||||||
|
/// select_like num 是否附加点赞 非必要 0:不点赞 1:同时点赞 默认为0
|
||||||
|
// csrf str CSRF Token(位于cookie) 必要
|
||||||
|
// https://api.bilibili.com/x/web-interface/coin/add
|
||||||
|
static const String coinVideo = '/x/web-interface/coin/add';
|
||||||
|
|
||||||
|
// 判断视频是否被投币(双端)GET
|
||||||
|
// access_key str APP登录Token APP方式必要
|
||||||
|
/// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
||||||
|
/// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
||||||
|
/// https://api.bilibili.com/x/web-interface/archive/coins
|
||||||
|
static const String hasCoinVideo = '/x/web-interface/archive/coins';
|
||||||
|
|
||||||
|
// 收藏视频(双端)POST
|
||||||
|
// access_key str APP登录Token APP方式必要
|
||||||
|
/// rid num 稿件avid 必要
|
||||||
|
/// type num 必须为2 必要
|
||||||
|
/// add_media_ids nums 需要加入的收藏夹mlid 非必要 同时添加多个,用,(%2C)分隔
|
||||||
|
/// del_media_ids nums 需要取消的收藏夹mlid 非必要 同时取消多个,用,(%2C)分隔
|
||||||
|
// csrf str CSRF Token(位于cookie) Cookie方式必要
|
||||||
|
// https://api.bilibili.com/medialist/gateway/coll/resource/deal
|
||||||
|
// https://api.bilibili.com/x/v3/fav/resource/deal
|
||||||
|
static const String favVideo = '/medialist/gateway/coll/resource/deal';
|
||||||
|
|
||||||
|
// 判断视频是否被收藏(双端)GET
|
||||||
|
/// aid
|
||||||
|
// https://api.bilibili.com/x/v2/fav/video/favoured
|
||||||
|
static const String hasFavVideo = '/x/v2/fav/video/favoured';
|
||||||
|
|
||||||
|
// 分享视频 (Web端) POST
|
||||||
|
// https://api.bilibili.com/x/web-interface/share/add
|
||||||
|
// aid num 稿件avid 必要(可选) avid与bvid任选一个
|
||||||
|
// bvid str 稿件bvid 必要(可选) avid与bvid任选一个
|
||||||
|
// csrf str CSRF Token(位于cookie) 必要
|
||||||
|
|
||||||
// 视频详情页 相关视频
|
// 视频详情页 相关视频
|
||||||
static const String relatedList = '/x/web-interface/archive/related';
|
static const String relatedList = '/x/web-interface/archive/related';
|
||||||
@ -40,4 +100,7 @@ class Api {
|
|||||||
/// tid int 分区id
|
/// tid int 分区id
|
||||||
// https://api.bilibili.com/x/v3/fav/resource/list?media_id=76614671&pn=1&ps=20&keyword=&order=mtime&type=0&tid=0
|
// https://api.bilibili.com/x/v3/fav/resource/list?media_id=76614671&pn=1&ps=20&keyword=&order=mtime&type=0&tid=0
|
||||||
static const String userFavFolderDetail = '/x/v3/fav/resource/list';
|
static const String userFavFolderDetail = '/x/v3/fav/resource/list';
|
||||||
|
|
||||||
|
// 正在直播的up & 关注的up
|
||||||
|
// https://api.bilibili.com/x/polymer/web-dynamic/v1/portal
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,6 +55,17 @@ class Request {
|
|||||||
dio.interceptors.add(cookieManager);
|
dio.interceptors.add(cookieManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从cookie中获取 csrf token
|
||||||
|
static Future<String> getCsrf() async {
|
||||||
|
var cookies = await cookieManager.cookieJar
|
||||||
|
.loadForRequest(Uri.parse(HttpString.baseApiUrl));
|
||||||
|
// for (var i in cookies) {
|
||||||
|
// print(i);
|
||||||
|
// }
|
||||||
|
var token = cookies.firstWhere((e) => e.name == 'bili_jct').value;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* config it and create
|
* config it and create
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:pilipala/http/api.dart';
|
import 'package:pilipala/http/api.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import 'package:pilipala/http/init.dart';
|
||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import 'package:pilipala/models/model_hot_video_item.dart';
|
||||||
@ -86,8 +90,75 @@ class VideoHttp {
|
|||||||
list.add(HotVideoItemModel.fromJson(i));
|
list.add(HotVideoItemModel.fromJson(i));
|
||||||
}
|
}
|
||||||
return {'status': true, 'data': list};
|
return {'status': true, 'data': list};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': []};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取点赞状态
|
||||||
|
static Future hasLikeVideo({required String aid}) async {
|
||||||
|
var res = await Request().get(Api.hasLikeVideo, data: {'aid': aid});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': []};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取投币状态
|
||||||
|
static Future hasCoinVideo({required String aid}) async {
|
||||||
|
var res = await Request().get(Api.hasCoinVideo, data: {'aid': aid});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
return {'status': true, 'data': []};
|
return {'status': true, 'data': []};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取收藏状态
|
||||||
|
static Future hasFavVideo({required String aid}) async {
|
||||||
|
var res = await Request().get(Api.hasFavVideo, data: {'aid': aid});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': []};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一键三连
|
||||||
|
// (取消)点赞
|
||||||
|
static Future likeVideo({required String aid, required bool type}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.likeVideo,
|
||||||
|
data: {
|
||||||
|
'aid': aid,
|
||||||
|
'like': type ? 1 : 2,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (取消)收藏
|
||||||
|
static Future favVideo(
|
||||||
|
{required String aid, required bool type, required String ids}) async {
|
||||||
|
Map data = {'rid': aid, 'type': 2};
|
||||||
|
// type true 添加收藏 false 取消收藏
|
||||||
|
if (type) {
|
||||||
|
data['add_media_ids'] = ids;
|
||||||
|
} else {
|
||||||
|
data['del_media_ids'] = ids;
|
||||||
|
}
|
||||||
|
var res = await Request()
|
||||||
|
.post(Api.favVideo, data: {'aid': aid, 'like': type ? 1 : 2});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': []};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,18 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/user.dart';
|
||||||
|
import 'package:pilipala/models/user/fav_folder.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class FavController extends GetxController {}
|
class FavController extends GetxController {
|
||||||
|
Rx<FavFolderData> favFolderData = FavFolderData().obs;
|
||||||
|
|
||||||
|
Future<dynamic> queryFavFolder() async {
|
||||||
|
var res = await await UserHttp.userfavFolder(
|
||||||
|
pn: 1,
|
||||||
|
ps: 10,
|
||||||
|
mid: GStrorage.user.get(UserBoxKey.userMid),
|
||||||
|
);
|
||||||
|
favFolderData.value = res['data'];
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/pages/fav/index.dart';
|
||||||
|
|
||||||
class FavPage extends StatefulWidget {
|
class FavPage extends StatefulWidget {
|
||||||
const FavPage({super.key});
|
const FavPage({super.key});
|
||||||
@ -8,12 +11,64 @@ class FavPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FavPageState extends State<FavPage> {
|
class _FavPageState extends State<FavPage> {
|
||||||
|
final FavController _favController = Get.put(FavController());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: Text('我的收藏'),
|
title: const Text('我的收藏'),
|
||||||
|
),
|
||||||
|
body: FutureBuilder(
|
||||||
|
future: _favController.queryFavFolder(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
Map data = snapshot.data as Map;
|
||||||
|
if (data['status']) {
|
||||||
|
return Obx(
|
||||||
|
() => ListView.builder(
|
||||||
|
itemCount: _favController.favFolderData.value.list!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ListTile(
|
||||||
|
onTap: () => Get.toNamed(
|
||||||
|
'/favDetail',
|
||||||
|
arguments:
|
||||||
|
_favController.favFolderData.value.list![index],
|
||||||
|
parameters: {
|
||||||
|
'mediaId': _favController
|
||||||
|
.favFolderData.value.list![index].id
|
||||||
|
.toString(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
leading: const Icon(Icons.folder_special_outlined),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
title: Text(_favController
|
||||||
|
.favFolderData.value.list![index].title!),
|
||||||
|
subtitle: Text(
|
||||||
|
'${_favController.favFolderData.value.list![index].mediaCount}个内容',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelSmall!
|
||||||
|
.fontSize),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => setState(() {}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return Text('请求中');
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,12 +56,12 @@ class MainController extends GetxController {
|
|||||||
// 'icon': const Icon(Icons.person_outline),
|
// 'icon': const Icon(Icons.person_outline),
|
||||||
// 'selectedIcon': const Icon(Icons.person),
|
// 'selectedIcon': const Icon(Icons.person),
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
CupertinoIcons.tray_full,
|
CupertinoIcons.folder,
|
||||||
size: 21,
|
size: 20,
|
||||||
),
|
),
|
||||||
'selectedIcon': const Icon(
|
'selectedIcon': const Icon(
|
||||||
CupertinoIcons.tray_full_fill,
|
CupertinoIcons.folder_fill,
|
||||||
size: 21,
|
size: 20,
|
||||||
),
|
),
|
||||||
'label': "媒体库",
|
'label': "媒体库",
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,12 +10,12 @@ class MediaController extends GetxController {
|
|||||||
{
|
{
|
||||||
'icon': Icons.file_download_outlined,
|
'icon': Icons.file_download_outlined,
|
||||||
'title': '离线缓存',
|
'title': '离线缓存',
|
||||||
'onTap': () => Get.toNamed('/fav'),
|
'onTap': () {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': Icons.history,
|
'icon': Icons.history,
|
||||||
'title': '观看记录',
|
'title': '观看记录',
|
||||||
'onTap': () => Get.toNamed('/fav'),
|
'onTap': () {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': Icons.star_border,
|
'icon': Icons.star_border,
|
||||||
@ -24,8 +24,8 @@ class MediaController extends GetxController {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': Icons.watch_later_outlined,
|
'icon': Icons.watch_later_outlined,
|
||||||
'title': '稍后再看',
|
'title': '稍候再看',
|
||||||
'onTap': () => Get.toNamed('/fav'),
|
'onTap': () => {},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -56,8 +56,13 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
color: primary,
|
color: primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.only(left: 15, top: 2, bottom: 2),
|
||||||
minLeadingWidth: 0,
|
minLeadingWidth: 0,
|
||||||
title: Text(i['title']),
|
title: Text(
|
||||||
|
i['title'],
|
||||||
|
style: const TextStyle(fontSize: 15),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
favFolder()
|
favFolder()
|
||||||
@ -105,13 +110,11 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: Padding(
|
trailing: IconButton(
|
||||||
padding: const EdgeInsets.only(right: 10),
|
onPressed: () => _mediaController.queryFavFolder(),
|
||||||
child: Text(
|
icon: const Icon(
|
||||||
'查看全部',
|
Icons.refresh,
|
||||||
style: TextStyle(
|
size: 20,
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
@ -26,6 +28,13 @@ class VideoIntroController extends GetxController {
|
|||||||
// up主粉丝数
|
// up主粉丝数
|
||||||
Map userStat = {'follower': '-'};
|
Map userStat = {'follower': '-'};
|
||||||
|
|
||||||
|
// 是否点赞
|
||||||
|
RxBool hasLike = false.obs;
|
||||||
|
// 是否投币
|
||||||
|
RxBool hasCoin = false.obs;
|
||||||
|
// 是否收藏
|
||||||
|
RxBool hasFav = false.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@ -57,6 +66,13 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
// 获取到粉丝数再返回
|
// 获取到粉丝数再返回
|
||||||
await queryUserStat();
|
await queryUserStat();
|
||||||
|
// 获取点赞状态
|
||||||
|
queryHasLikeVideo();
|
||||||
|
// 获取投币状态
|
||||||
|
queryHasCoinVideo();
|
||||||
|
// 获取收藏状态
|
||||||
|
queryHasFavVideo();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,4 +83,51 @@ class VideoIntroController extends GetxController {
|
|||||||
userStat = result['data'];
|
userStat = result['data'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取点赞状态
|
||||||
|
Future queryHasLikeVideo() async {
|
||||||
|
var result = await VideoHttp.hasLikeVideo(aid: aid);
|
||||||
|
// data num 被点赞标志 0:未点赞 1:已点赞
|
||||||
|
hasLike.value = result["data"] == 1 ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取投币状态
|
||||||
|
Future queryHasCoinVideo() async {
|
||||||
|
var result = await VideoHttp.hasCoinVideo(aid: aid);
|
||||||
|
hasCoin.value = result["data"]['multiply'] == 0 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取收藏状态
|
||||||
|
Future queryHasFavVideo() async {
|
||||||
|
var result = await VideoHttp.hasFavVideo(aid: aid);
|
||||||
|
hasFav.value = result["data"]['favoured'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一键三连
|
||||||
|
|
||||||
|
// (取消)点赞
|
||||||
|
Future actionLikeVideo() async {
|
||||||
|
var result = await VideoHttp.likeVideo(aid: aid, type: !hasLike.value);
|
||||||
|
if (result['status']) {
|
||||||
|
hasLike.value = result["data"] == 1 ? true : false;
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 投币
|
||||||
|
Future actionCoinVideo() async {
|
||||||
|
print('投币');
|
||||||
|
}
|
||||||
|
|
||||||
|
// (取消)收藏
|
||||||
|
Future actionFavVideo() async {
|
||||||
|
print('(取消)收藏');
|
||||||
|
// var result = await VideoHttp.favVideo(aid: aid, type: true, ids: '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分享视频
|
||||||
|
Future actionShareVideo() async {
|
||||||
|
print('分享视频');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import 'package:get/get.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/pages/fav/index.dart';
|
||||||
|
import 'package:pilipala/pages/favDetail/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/widgets/expandable_section.dart';
|
import 'package:pilipala/pages/video/detail/widgets/expandable_section.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||||
@ -100,6 +102,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
/// 手动控制
|
/// 手动控制
|
||||||
late Animation<double>? _manualAnimation;
|
late Animation<double>? _manualAnimation;
|
||||||
|
|
||||||
|
final FavController _favController = Get.put(FavController());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -113,6 +117,102 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
Tween<double>(begin: 0.5, end: 1.5).animate(_manualController!);
|
Tween<double>(begin: 0.5, end: 1.5).animate(_manualController!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showFavBottomSheet() {
|
||||||
|
Get.bottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
Container(
|
||||||
|
height: 450,
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AppBar(
|
||||||
|
toolbarHeight: 50,
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
centerTitle: false,
|
||||||
|
elevation: 1,
|
||||||
|
title: Text(
|
||||||
|
'选择文件夹',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('完成'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Material(
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: _favController.queryFavFolder(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
Map data = snapshot.data as Map;
|
||||||
|
if (data['status']) {
|
||||||
|
return Obx(
|
||||||
|
() => ListView.builder(
|
||||||
|
itemCount: _favController
|
||||||
|
.favFolderData.value.list!.length +
|
||||||
|
1,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return const SizedBox(height: 15);
|
||||||
|
} else {
|
||||||
|
return ListTile(
|
||||||
|
onTap: () {},
|
||||||
|
dense: true,
|
||||||
|
leading:
|
||||||
|
const Icon(Icons.folder_special_outlined),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
title: Text(_favController.favFolderData.value
|
||||||
|
.list![index - 1].title!),
|
||||||
|
subtitle: Text(
|
||||||
|
'${_favController.favFolderData.value.list![index - 1].mediaCount}个内容',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline,
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelSmall!
|
||||||
|
.fontSize),
|
||||||
|
),
|
||||||
|
trailing: Transform.scale(
|
||||||
|
scale: 0.9,
|
||||||
|
child: Checkbox(
|
||||||
|
value: false,
|
||||||
|
onChanged: (bool? checkValue) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => setState(() {}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return Text('请求中');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
persistent: false,
|
||||||
|
backgroundColor: Theme.of(context).bottomSheetTheme.backgroundColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
@ -293,7 +393,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
_actionGrid(context),
|
_actionGrid(context, widget.videoIntroController),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: const Center(child: CircularProgressIndicator()),
|
: const Center(child: CircularProgressIndicator()),
|
||||||
@ -302,7 +402,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 喜欢 投币 分享
|
// 喜欢 投币 分享
|
||||||
Widget _actionGrid(BuildContext context) {
|
Widget _actionGrid(BuildContext context, videoIntroController) {
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: constraints.maxWidth / 5 * 0.8,
|
height: constraints.maxWidth / 5 * 0.8,
|
||||||
@ -312,39 +412,50 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
crossAxisCount: 5,
|
crossAxisCount: 5,
|
||||||
childAspectRatio: 1.25,
|
childAspectRatio: 1.25,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ActionItem(
|
Obx(
|
||||||
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||||
onTap: () => {},
|
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||||
selectStatus: false,
|
onTap: () => videoIntroController.actionLikeVideo(),
|
||||||
|
selectStatus: videoIntroController.hasLike.value,
|
||||||
loadingStatus: widget.loadingStatus,
|
loadingStatus: widget.loadingStatus,
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.videoDetail!.stat!.like!.toString()
|
? widget.videoDetail!.stat!.like!.toString()
|
||||||
: '-'),
|
: '-'),
|
||||||
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsDown),
|
icon: const Icon(FontAwesomeIcons.thumbsDown),
|
||||||
|
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
|
||||||
onTap: () => {},
|
onTap: () => {},
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: widget.loadingStatus,
|
loadingStatus: widget.loadingStatus,
|
||||||
text: '不喜欢'),
|
text: '不喜欢'),
|
||||||
ActionItem(
|
Obx(
|
||||||
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.b),
|
icon: const Icon(FontAwesomeIcons.b),
|
||||||
onTap: () => {},
|
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||||
selectStatus: false,
|
onTap: () => videoIntroController.actionCoinVideo(),
|
||||||
|
selectStatus: videoIntroController.hasCoin.value,
|
||||||
loadingStatus: widget.loadingStatus,
|
loadingStatus: widget.loadingStatus,
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.videoDetail!.stat!.coin!.toString()
|
? widget.videoDetail!.stat!.coin!.toString()
|
||||||
: '-'),
|
: '-'),
|
||||||
ActionItem(
|
),
|
||||||
|
Obx(
|
||||||
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.star),
|
icon: const Icon(FontAwesomeIcons.star),
|
||||||
onTap: () => {},
|
selectIcon: const Icon(FontAwesomeIcons.star),
|
||||||
selectStatus: false,
|
// onTap: () => videoIntroController.actionFavVideo(),
|
||||||
|
onTap: () => showFavBottomSheet(),
|
||||||
|
selectStatus: videoIntroController.hasFav.value,
|
||||||
loadingStatus: widget.loadingStatus,
|
loadingStatus: widget.loadingStatus,
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.videoDetail!.stat!.favorite!.toString()
|
? widget.videoDetail!.stat!.favorite!.toString()
|
||||||
: '-'),
|
: '-'),
|
||||||
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||||
onTap: () => {},
|
onTap: () => videoIntroController.actionShareVideo(),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: widget.loadingStatus,
|
loadingStatus: widget.loadingStatus,
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
@ -359,6 +470,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
class ActionItem extends StatelessWidget {
|
class ActionItem extends StatelessWidget {
|
||||||
Icon? icon;
|
Icon? icon;
|
||||||
|
Icon? selectIcon;
|
||||||
Function? onTap;
|
Function? onTap;
|
||||||
bool? loadingStatus;
|
bool? loadingStatus;
|
||||||
String? text;
|
String? text;
|
||||||
@ -367,6 +479,7 @@ class ActionItem extends StatelessWidget {
|
|||||||
ActionItem({
|
ActionItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.icon,
|
this.icon,
|
||||||
|
this.selectIcon,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.loadingStatus,
|
this.loadingStatus,
|
||||||
this.text,
|
this.text,
|
||||||
@ -378,16 +491,17 @@ class ActionItem extends StatelessWidget {
|
|||||||
return Material(
|
return Material(
|
||||||
child: Ink(
|
child: Ink(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: () => onTap!(),
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon!.icon!,
|
const SizedBox(height: 4),
|
||||||
size: 21,
|
selectStatus
|
||||||
color: selectStatus
|
? Icon(selectIcon!.icon!,
|
||||||
? Theme.of(context).primaryColor
|
size: 21, color: Theme.of(context).primaryColor)
|
||||||
: Theme.of(context).colorScheme.outline),
|
: Icon(icon!.icon!,
|
||||||
|
size: 21, color: Theme.of(context).colorScheme.outline),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
opacity: loadingStatus! ? 0 : 1,
|
opacity: loadingStatus! ? 0 : 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user