Merge branch 'design'
This commit is contained in:
@ -8,6 +8,7 @@ import 'package:pilipala/http/video.dart';
|
||||
import 'package:pilipala/models/member/archive.dart';
|
||||
import 'package:pilipala/models/member/coin.dart';
|
||||
import 'package:pilipala/models/member/info.dart';
|
||||
import 'package:pilipala/models/member/like.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
@ -25,6 +26,7 @@ class MemberController extends GetxController {
|
||||
RxInt attribute = (-1).obs;
|
||||
RxString attributeText = '关注'.obs;
|
||||
RxList<MemberCoinsDataModel> recentCoinsList = <MemberCoinsDataModel>[].obs;
|
||||
RxList<MemberLikeDataModel> recentLikeList = <MemberLikeDataModel>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@ -190,12 +192,17 @@ class MemberController extends GetxController {
|
||||
Share.share('${memberInfo.value.name} - https://space.bilibili.com/$mid');
|
||||
}
|
||||
|
||||
// 请求专栏
|
||||
// 请求合集
|
||||
Future getMemberSeasons() async {
|
||||
if (userInfo == null) return;
|
||||
var res = await MemberHttp.getMemberSeasons(mid, 1, 10);
|
||||
if (!res['status']) {
|
||||
SmartDialog.showToast("用户专栏请求异常:${res['msg']}");
|
||||
} else {
|
||||
// 只取前四个专栏
|
||||
res['data'].seasonsList.map((e) {
|
||||
e.archives = e.archives!.sublist(0, 4);
|
||||
}).toList();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -208,6 +215,14 @@ class MemberController extends GetxController {
|
||||
return res;
|
||||
}
|
||||
|
||||
// 请求点赞视频
|
||||
Future getRecentLikeVideo() async {
|
||||
if (userInfo == null) return;
|
||||
var res = await MemberHttp.getRecentLikeVideo(mid: mid);
|
||||
recentLikeList.value = res['data'];
|
||||
return res;
|
||||
}
|
||||
|
||||
// 跳转查看动态
|
||||
void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid');
|
||||
|
||||
|
@ -9,6 +9,7 @@ import 'package:pilipala/pages/member/index.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
import 'widgets/conis.dart';
|
||||
import 'widgets/like.dart';
|
||||
import 'widgets/profile.dart';
|
||||
import 'widgets/seasons.dart';
|
||||
|
||||
@ -26,6 +27,7 @@ class _MemberPageState extends State<MemberPage>
|
||||
late Future _futureBuilderFuture;
|
||||
late Future _memberSeasonsFuture;
|
||||
late Future _memberCoinsFuture;
|
||||
late Future _memberLikeFuture;
|
||||
final ScrollController _extendNestCtr = ScrollController();
|
||||
final StreamController<bool> appbarStream = StreamController<bool>();
|
||||
late int mid;
|
||||
@ -39,6 +41,7 @@ class _MemberPageState extends State<MemberPage>
|
||||
_futureBuilderFuture = _memberController.getInfo();
|
||||
_memberSeasonsFuture = _memberController.getMemberSeasons();
|
||||
_memberCoinsFuture = _memberController.getRecentCoinVideo();
|
||||
_memberLikeFuture = _memberController.getRecentLikeVideo();
|
||||
_extendNestCtr.addListener(
|
||||
() {
|
||||
final double offset = _extendNestCtr.position.pixels;
|
||||
@ -162,6 +165,7 @@ class _MemberPageState extends State<MemberPage>
|
||||
trailing:
|
||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
const Divider(height: 1, thickness: 0.1),
|
||||
|
||||
/// 视频
|
||||
ListTile(
|
||||
@ -170,45 +174,41 @@ class _MemberPageState extends State<MemberPage>
|
||||
trailing:
|
||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
const Divider(height: 1, thickness: 0.1),
|
||||
|
||||
/// 专栏
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
title: const Text('Ta的专栏'),
|
||||
),
|
||||
const ListTile(title: Text('Ta的专栏')),
|
||||
const Divider(height: 1, thickness: 0.1),
|
||||
|
||||
/// 合集
|
||||
const ListTile(title: Text('Ta的合集')),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: _memberSeasonsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['data'].seasonsList.isEmpty) {
|
||||
return commenWidget('用户没有设置专栏');
|
||||
} else {
|
||||
return MemberSeasonsPanel(data: data['data']);
|
||||
}
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
child: FutureBuilder(
|
||||
future: _memberSeasonsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['data'].seasonsList.isEmpty) {
|
||||
return commenWidget('用户没有设置合集');
|
||||
} else {
|
||||
return MemberSeasonsPanel(data: data['data']);
|
||||
}
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@ -218,12 +218,7 @@ class _MemberPageState extends State<MemberPage>
|
||||
/// 最近投币
|
||||
Obx(
|
||||
() => _memberController.recentCoinsList.isNotEmpty
|
||||
? ListTile(
|
||||
onTap: () {},
|
||||
title: const Text('最近投币的视频'),
|
||||
// trailing: const Icon(Icons.arrow_forward_outlined,
|
||||
// size: 19),
|
||||
)
|
||||
? const ListTile(title: Text('最近投币的视频'))
|
||||
: const SizedBox(),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
@ -257,13 +252,44 @@ class _MemberPageState extends State<MemberPage>
|
||||
),
|
||||
),
|
||||
),
|
||||
// 最近点赞
|
||||
// ListTile(
|
||||
// onTap: () {},
|
||||
// title: const Text('最近点赞的视频'),
|
||||
// trailing:
|
||||
// const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
// ),
|
||||
|
||||
/// 最近点赞
|
||||
Obx(
|
||||
() => _memberController.recentLikeList.isNotEmpty
|
||||
? const ListTile(title: Text('最近点赞的视频'))
|
||||
: const SizedBox(),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: _memberLikeFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
return MemberLikePanel(data: data['data']);
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -4,8 +4,8 @@ import 'package:pilipala/models/member/coin.dart';
|
||||
import 'package:pilipala/pages/member_coin/widgets/item.dart';
|
||||
|
||||
class MemberCoinsPanel extends StatelessWidget {
|
||||
final List<MemberCoinsDataModel>? data;
|
||||
const MemberCoinsPanel({super.key, this.data});
|
||||
final List<MemberCoinsDataModel> data;
|
||||
const MemberCoinsPanel({super.key, required this.data});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -20,9 +20,9 @@ class MemberCoinsPanel extends StatelessWidget {
|
||||
),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: data!.length,
|
||||
itemCount: data.length,
|
||||
itemBuilder: (context, i) {
|
||||
return MemberCoinsItem(coinItem: data![i]);
|
||||
return MemberCoinsItem(coinItem: data[i]);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
31
lib/pages/member/widgets/like.dart
Normal file
31
lib/pages/member/widgets/like.dart
Normal file
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/models/member/like.dart';
|
||||
import 'package:pilipala/pages/member_like/widgets/item.dart';
|
||||
|
||||
class MemberLikePanel extends StatelessWidget {
|
||||
final List<MemberLikeDataModel> data;
|
||||
const MemberLikePanel({super.key, required this.data});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, boxConstraints) {
|
||||
return GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2, // Use a fixed count for GridView
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
childAspectRatio: 0.94,
|
||||
),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: data.length,
|
||||
itemBuilder: (context, i) {
|
||||
return MemberLikeItem(likeItem: data[i]);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ class MemberSeasonsPanel extends StatelessWidget {
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () => Get.toNamed(
|
||||
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
|
||||
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}&seasonName=${item.meta!.name}'),
|
||||
title: Text(
|
||||
item.meta!.name!,
|
||||
maxLines: 1,
|
||||
@ -44,24 +44,30 @@ class MemberSeasonsPanel extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
LayoutBuilder(
|
||||
builder: (context, boxConstraints) {
|
||||
return GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2, // Use a fixed count for GridView
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
childAspectRatio: 0.94,
|
||||
),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: item.archives!.length,
|
||||
itemBuilder: (context, i) {
|
||||
return MemberSeasonsItem(seasonItem: item.archives![i]);
|
||||
},
|
||||
);
|
||||
},
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, boxConstraints) {
|
||||
return GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2, // Use a fixed count for GridView
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
childAspectRatio: 0.94,
|
||||
),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: item.archives!.length,
|
||||
itemBuilder: (context, i) {
|
||||
return MemberSeasonsItem(seasonItem: item.archives![i]);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
96
lib/pages/member_like/widgets/item.dart
Normal file
96
lib/pages/member_like/widgets/item.dart
Normal file
@ -0,0 +1,96 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/badge.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||
import 'package:pilipala/http/search.dart';
|
||||
import 'package:pilipala/models/member/like.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class MemberLikeItem extends StatelessWidget {
|
||||
final MemberLikeDataModel likeItem;
|
||||
|
||||
const MemberLikeItem({
|
||||
Key? key,
|
||||
required this.likeItem,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String heroTag = Utils.makeHeroTag(likeItem.aid);
|
||||
return Card(
|
||||
elevation: 0,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
margin: EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
int cid =
|
||||
await SearchHttp.ab2c(aid: likeItem.aid, bvid: likeItem.bvid);
|
||||
Get.toNamed('/video?bvid=${likeItem.bvid}&cid=$cid',
|
||||
arguments: {'videoItem': likeItem, 'heroTag': heroTag});
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||
double maxWidth = boxConstraints.maxWidth;
|
||||
double maxHeight = boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: likeItem.pic,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
if (likeItem.duration != null)
|
||||
PBadge(
|
||||
bottom: 6,
|
||||
right: 6,
|
||||
type: 'gray',
|
||||
text: Utils.timeFormat(likeItem.duration),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(5, 6, 0, 0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
likeItem.title!,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
StatView(
|
||||
view: likeItem.stat!.view,
|
||||
theme: 'gray',
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.CustomStamp_str(
|
||||
timestamp: likeItem.pubdate, date: 'MM-DD'),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -43,7 +43,8 @@ class _MemberSeasonsPageState extends State<MemberSeasonsPage> {
|
||||
appBar: AppBar(
|
||||
titleSpacing: 0,
|
||||
centerTitle: false,
|
||||
title: Text('他的专栏', style: Theme.of(context).textTheme.titleMedium),
|
||||
title: Text(Get.parameters['seasonName']!,
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
|
Reference in New Issue
Block a user