mod: 用户页跳转&样式修改

This commit is contained in:
guozhigq
2023-07-14 22:44:21 +08:00
parent 8fd1efd8bf
commit b7083fdc15
17 changed files with 764 additions and 227 deletions

View File

@ -199,6 +199,8 @@ class Api {
// 用户信息 需要Wbi签名
// https://api.bilibili.com/x/space/wbi/acc/info?mid=503427686&token=&platform=web&web_location=1550101&w_rid=d709892496ce93e3d94d6d37c95bde91&wts=1689301482
static const String memberInfo =
'https://api.bilibili.com/x/space/wbi/acc/info';
static const String memberInfo = '/x/space/wbi/acc/info';
// 用户名片信息
static const String memberCardInfo = '/x/web-interface/card';
}

View File

@ -19,10 +19,23 @@ class MemberHttp {
}
static Future memberStat({int? mid}) async {
var res = await Request().get(Api.userStat, data: {mid: mid});
var res = await Request().get(Api.userStat, data: {'vmid': mid});
if (res.data['code'] == 0) {
print(res.data['data']);
// return {'status': true, 'data': FansDataModel.fromJson(res.data['data'])};
return {'status': true, 'data': res.data['data']};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
static Future memberCardInfo({int? mid}) async {
var res = await Request()
.get(Api.memberCardInfo, data: {'mid': mid, 'photo': true});
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
return {
'status': false,

View File

@ -7,6 +7,9 @@ class MemberInfoModel {
this.sign,
this.level,
this.isFollowed,
this.topPhoto,
this.official,
this.vip,
});
int? mid;
@ -16,14 +19,73 @@ class MemberInfoModel {
String? sign;
int? level;
bool? isFollowed;
String? topPhoto;
Map? official;
Vip? vip;
LiveRoom? liveRoom;
MemberInfoModel.fromJson(Map<String, dynamic> json) {
mid = json['mid'];
name = json['name'];
sex = json['sex'];
face = json['face'];
sign = json['sign'];
sign = json['sign'] == '' ? '该用户还没有签名' : json['sign'];
level = json['level'];
isFollowed = json['is_followed'];
topPhoto = json['top_photo'];
official = json['official'];
vip = Vip.fromJson(json['vip']);
liveRoom = LiveRoom.fromJson(json['live_room']);
}
}
class Vip {
Vip({
this.type,
this.status,
this.dueDate,
this.label,
});
int? type;
int? status;
int? dueDate;
Map? label;
Vip.fromJson(Map<String, dynamic> json) {
type = json['type'];
status = json['status'];
dueDate = json['due_date'];
label = json['label'];
}
}
class LiveRoom {
LiveRoom({
this.roomStatus,
this.liveStatus,
this.url,
this.title,
this.cover,
this.roomId,
this.roundStatus,
});
int? roomStatus;
int? liveStatus;
String? url;
String? title;
String? cover;
int? roomId;
int? roundStatus;
LiveRoom.fromJson(Map<String, dynamic> json) {
roomStatus = json['room_status'];
liveStatus = json['live_status'];
url = json['url'];
title = json['title'];
cover = json['cover'];
roomId = json['room_id'];
roundStatus = json['round_status'];
}
}

View File

@ -193,7 +193,7 @@ class SearchUserItemModel {
usign = json['usign'];
fans = json['fans'];
videos = json['videos'];
upic = json['upic'];
upic = 'https:' + json['upic'];
faceNft = json['face_nft'];
faceNftType = json['face_nft_type'];
verifyInfo = json['verify_info'];

View File

@ -1,20 +1,29 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
Widget author(item, context) {
String heroTag = Utils.makeHeroTag(item.modules.moduleAuthor.mid);
return Container(
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
child: Row(
children: [
GestureDetector(
onTap: () =>
Get.toNamed('/member?mid=${item.modules.moduleAuthor.mid}'),
child: NetworkImgLayer(
width: 40,
height: 40,
type: 'avatar',
src: item.modules.moduleAuthor.face,
onTap: () => Get.toNamed(
'/member?mid=${item.modules.moduleAuthor.mid}',
arguments: {
'face': item.modules.moduleAuthor.face,
'heroTag': heroTag
}),
child: Hero(
tag: heroTag,
child: NetworkImgLayer(
width: 40,
height: 40,
type: 'avatar',
src: item.modules.moduleAuthor.face,
),
),
),
const SizedBox(width: 10),

View File

@ -174,12 +174,13 @@ class _UpPanelState extends State<UpPanel> {
Badge(
smallSize: 8,
label: data.type == 'live' ? const Text('Live') : null,
textColor: Theme.of(context).colorScheme.onPrimary,
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
alignment: AlignmentDirectional.bottomCenter,
padding: const EdgeInsets.only(left: 4, right: 4),
padding: const EdgeInsets.only(left: 6, right: 6),
isLabelVisible: data.type == 'live' ||
(data.type == 'up' && (data.hasUpdate ?? false)),
backgroundColor: Theme.of(context).primaryColor,
backgroundColor:
Theme.of(context).colorScheme.secondaryContainer,
child: NetworkImgLayer(
width: 49,
height: 49,

View File

@ -1,14 +1,21 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
Widget fanItem({item}) {
String heroTag = Utils.makeHeroTag(item!.mid);
return ListTile(
onTap: () {},
leading: NetworkImgLayer(
width: 38,
height: 38,
type: 'avatar',
src: item.face,
onTap: () => Get.toNamed('/member?mid=${item.mid}',
arguments: {'face': item.face, 'heroTag': heroTag}),
leading: Hero(
tag: heroTag,
child: NetworkImgLayer(
width: 38,
height: 38,
type: 'avatar',
src: item.face,
),
),
title: Text(item.uname),
subtitle: Text(

View File

@ -1,15 +1,21 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
Widget followItem({item}) {
String heroTag = Utils.makeHeroTag(item!.mid);
return ListTile(
onTap: () {},
leading: NetworkImgLayer(
width: 38,
height: 38,
type: 'avatar',
src: item.face,
onTap: () => Get.toNamed('/member?mid=${item.mid}',
arguments: {'face': item.face, 'heroTag': heroTag}),
leading: Hero(
tag: heroTag,
child: NetworkImgLayer(
width: 38,
height: 38,
type: 'avatar',
src: item.face,
),
),
title: Text(item.uname),
subtitle: Text(

View File

@ -23,7 +23,7 @@ class LiveRoomController extends GetxController {
super.onInit();
if (Get.arguments != null) {
var args = Get.arguments['liveItem'];
heroTag = Get.arguments['heroTag'];
heroTag = Get.arguments['heroTag'] ?? '';
liveItem = args;
roomId = liveItem.roomId!;
if (args.pic != null && args.pic != '') {

View File

@ -89,20 +89,21 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
controller: _meeduPlayerController!,
),
),
Visibility(
visible: isShowCover,
child: Positioned(
top: 0,
left: 0,
right: 0,
child: NetworkImgLayer(
type: 'emote',
src: _liveRoomController.liveItem.cover,
width: Get.size.width,
height: videoHeight,
if (_liveRoomController.liveItem.cover != null)
Visibility(
visible: isShowCover,
child: Positioned(
top: 0,
left: 0,
right: 0,
child: NetworkImgLayer(
type: 'emote',
src: _liveRoomController.liveItem.cover,
width: Get.size.width,
height: videoHeight,
),
),
),
),
],
)),
if (_liveRoomController.liveItem.watchedShow != null)

View File

@ -1,18 +1,26 @@
import 'package:get/get.dart';
import 'package:pilipala/http/member.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/utils/wbi_sign.dart';
class MemberController extends GetxController {
late int mid;
Rx<MemberInfoModel> memberInfo = MemberInfoModel().obs;
Map? userStat;
String? face;
String? heroTag;
@override
void onInit() {
super.onInit();
mid = int.parse(Get.parameters['mid']!);
getInfo();
face = Get.arguments['face']!;
heroTag = Get.arguments['heroTag']!;
}
getInfo() async {
// 获取用户信息
Future<Map<String, dynamic>> getInfo() async {
await getMemberStat();
String params = await WbiSign().makSign({
'mid': mid,
'token': '',
@ -22,7 +30,25 @@ class MemberController extends GetxController {
params = '?$params';
var res = await MemberHttp.memberInfo(params: params);
if (res['status']) {
print(res['data']);
memberInfo.value = res['data'];
}
return res;
}
// 获取用户状态
Future<Map<String, dynamic>> getMemberStat() async {
var res = await MemberHttp.memberStat(mid: mid);
if (res['status']) {
userStat = res['data'];
}
return res;
}
Future getMemberCardInfo() async {
var res = await MemberHttp.memberCardInfo(mid: mid);
if (res['status']) {
print(userStat);
}
return res;
}
}

View File

@ -1,7 +1,10 @@
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/user/stat.dart';
import 'package:pilipala/pages/member/index.dart';
import 'package:pilipala/utils/utils.dart';
class MemberPage extends StatefulWidget {
const MemberPage({super.key});
@ -10,11 +13,379 @@ class MemberPage extends StatefulWidget {
State<MemberPage> createState() => _MemberPageState();
}
class _MemberPageState extends State<MemberPage> {
class _MemberPageState extends State<MemberPage>
with SingleTickerProviderStateMixin {
final MemberController _memberController = Get.put(MemberController());
final ScrollController _extendNestCtr = ScrollController();
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold();
return Scaffold(
primary: true,
body: ExtendedNestedScrollView(
controller: _extendNestCtr,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
pinned: false,
primary: true,
elevation: 0,
scrolledUnderElevation: 0,
forceElevated: innerBoxIsScrolled,
expandedHeight: 300,
actions: [
IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert)),
const SizedBox(width: 4),
],
flexibleSpace: FlexibleSpaceBar(
background: Stack(
children: [
Positioned.fill(
bottom: 10,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fitWidth,
image: NetworkImage(_memberController.face!),
alignment: Alignment.topCenter,
isAntiAlias: true,
),
),
foregroundDecoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context)
.colorScheme
.background
.withOpacity(0.44),
Theme.of(context).colorScheme.background,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [0.0, 0.46],
),
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
height: 20,
child: Container(
color: Theme.of(context).colorScheme.background,
),
),
Padding(
padding: const EdgeInsets.only(left: 18, right: 18),
child: FutureBuilder(
future: _memberController.getInfo(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
Map data = snapshot.data!;
if (data['status']) {
return Obx(
() => Stack(
alignment: AlignmentDirectional.center,
children: [
Column(
// mainAxisAlignment:
// MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
profile(
_memberController.memberInfo.value),
const SizedBox(height: 14),
Row(
children: [
Text(
_memberController
.memberInfo.value.name!,
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
fontWeight:
FontWeight.bold),
),
const SizedBox(width: 6),
Image.asset(
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
height: 11,
),
const SizedBox(width: 6),
if (_memberController.memberInfo
.value.vip!.status ==
1 &&
_memberController.memberInfo
.value.vip!.label![
'img_label_uri_hans'] !=
'') ...[
Image.network(
_memberController.memberInfo
.value.vip!.label![
'img_label_uri_hans'],
height: 20,
),
] else if (_memberController
.memberInfo
.value
.vip!
.status ==
1 &&
_memberController.memberInfo
.value.vip!.label![
'img_label_uri_hans_static'] !=
'') ...[
Image.network(
_memberController.memberInfo
.value.vip!.label![
'img_label_uri_hans_static'],
height: 20,
),
]
],
),
if (_memberController.memberInfo.value
.official!['title'] !=
'') ...[
const SizedBox(height: 6),
Row(
children: [
Text(
_memberController
.memberInfo
.value
.official![
'role'] ==
1
? '个人认证:'
: '企业认证:',
),
Text(
_memberController.memberInfo
.value.official!['title']!,
),
],
),
],
const SizedBox(height: 4),
if (_memberController
.memberInfo.value.sign !=
'')
Text(
_memberController
.memberInfo.value.sign!,
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
],
),
);
} else {
return SizedBox();
}
} else {
// 骨架屏
return profile(null, loadingStatus: true);
}
},
),
)
],
),
),
),
];
},
pinnedHeaderSliverHeightBuilder: () {
return MediaQuery.of(context).padding.top + kToolbarHeight;
},
onlyOneScrollInBody: true,
body: Column(
children: [
Container(
width: double.infinity,
height: 50,
child: TabBar(controller: _tabController, tabs: [
Tab(text: '主页'),
Tab(text: '动态'),
Tab(text: '投稿'),
]),
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
Text('主页'),
Text('动态'),
Text('投稿'),
],
))
],
),
),
);
}
Widget profile(memberInfo, {loadingStatus = false}) {
return Padding(
padding: EdgeInsets.only(top: 3 * MediaQuery.of(context).padding.top),
child: Row(
children: [
Hero(
tag: _memberController.heroTag!,
child: Stack(
children: [
NetworkImgLayer(
width: 90,
height: 90,
type: 'avatar',
src: !loadingStatus
? memberInfo.face
: _memberController.face,
),
Positioned(
bottom: 0,
left: 14,
child: Container(
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius:
const BorderRadius.all(Radius.circular(10)),
),
child: Row(children: [
Image.asset(
'assets/images/live.gif',
height: 10,
),
Text(
' 直播中',
style: TextStyle(
color: Colors.white,
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize),
)
]),
),
)
],
)),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text(
!loadingStatus
? _memberController.userStat!['following']
.toString()
: '-',
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(
'关注',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
Column(
children: [
Text(
!loadingStatus
? Utils.numFormat(
_memberController.userStat!['follower'],
)
: '-',
style:
const TextStyle(fontWeight: FontWeight.bold)),
Text('粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize))
],
),
Column(
children: [
const Text('-',
style: TextStyle(fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
],
),
),
const SizedBox(height: 10),
Row(
children: [
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 42, right: 42),
foregroundColor: !loadingStatus && memberInfo.isFollowed
? null
: Theme.of(context).colorScheme.onPrimary,
backgroundColor: !loadingStatus && memberInfo.isFollowed
? Theme.of(context).colorScheme.onInverseSurface
: Theme.of(context).colorScheme.primary, // 设置按钮背景色
),
child: Text(!loadingStatus && memberInfo.isFollowed
? '取关'
: '关注'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 42, right: 42),
backgroundColor:
Theme.of(context).colorScheme.onInverseSurface,
),
child: const Text('发消息'),
)
],
)
],
),
),
],
),
);
}
}

View File

@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
Widget searchUserPanel(BuildContext context, ctr, list) {
TextStyle style = TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
color: Theme.of(context).colorScheme.outline);
return ListView.builder(
controller: ctr!.scrollController,
addAutomaticKeepAlives: false,
@ -12,17 +15,22 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
itemCount: list!.length,
itemBuilder: (context, index) {
var i = list![index];
String heroTag = Utils.makeHeroTag(i!.mid);
return InkWell(
onTap: () {},
onTap: () => Get.toNamed('/member?mid=${i.mid}',
arguments: {'heroTag': heroTag, 'face': i.upic}),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Row(
children: [
NetworkImgLayer(
width: 42,
height: 42,
src: i.upic,
type: 'avatar',
Hero(
tag: heroTag,
child: NetworkImgLayer(
width: 42,
height: 42,
src: i.upic,
type: 'avatar',
),
),
const SizedBox(width: 10),
Column(

View File

@ -360,62 +360,80 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
height: 26,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
Row(
children: [
NetworkImgLayer(
type: 'avatar',
src: !widget.loadingStatus
? widget.videoDetail!.owner!.face
: videoItem['owner'].face,
width: 38,
height: 38,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(!widget.loadingStatus
? widget.videoDetail!.owner!.name
: videoItem['owner'].name),
// const SizedBox(width: 10),
Text(
widget.loadingStatus
? '- 粉丝'
: '${Utils.numFormat(videoIntroController.userStat['follower'])}粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize,
color: Theme.of(context).colorScheme.outline),
),
],
),
const Spacer(),
AnimatedOpacity(
opacity: widget.loadingStatus ? 0 : 1,
duration: const Duration(milliseconds: 150),
child: SizedBox(
height: 36,
child: Obx(
() => videoIntroController.followStatus.isNotEmpty
? ElevatedButton(
onPressed: () => videoIntroController
.actionRelationMod(),
child: Text(videoIntroController
.followStatus['attribute'] ==
0
? '关注'
: '已关注'),
)
: const SizedBox(),
GestureDetector(
onTap: () {
int mid = !widget.loadingStatus
? widget.videoDetail!.owner!.mid
: videoItem['owner'].mid;
String face = !widget.loadingStatus
? widget.videoDetail!.owner!.face
: videoItem['owner'].face;
Get.toNamed('/member?mid=$mid', arguments: {
'face': face,
'heroTag': (mid + 99).toString()
});
},
child: Row(
children: [
Hero(
tag: videoItem['owner'].mid + 99,
child: NetworkImgLayer(
type: 'avatar',
src: !widget.loadingStatus
? widget.videoDetail!.owner!.face
: videoItem['owner'].face,
width: 38,
height: 38,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
),
),
),
],
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(!widget.loadingStatus
? widget.videoDetail!.owner!.name
: videoItem['owner'].name),
// const SizedBox(width: 10),
Text(
widget.loadingStatus
? '- 粉丝'
: '${Utils.numFormat(videoIntroController.userStat['follower'])}粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize,
color: Theme.of(context).colorScheme.outline),
),
],
),
const Spacer(),
AnimatedOpacity(
opacity: widget.loadingStatus ? 0 : 1,
duration: const Duration(milliseconds: 150),
child: SizedBox(
height: 36,
child: Obx(
() => videoIntroController.followStatus.isNotEmpty
? ElevatedButton(
onPressed: () => videoIntroController
.actionRelationMod(),
child: Text(videoIntroController
.followStatus['attribute'] ==
0
? '关注'
: '已关注'),
)
: const SizedBox(),
),
),
),
],
),
),
Divider(
height: 26,
color: Theme.of(context).dividerColor.withOpacity(0.1),

View File

@ -128,115 +128,112 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
_videoReplyController.currentPage = 0;
return await _videoReplyController.queryReplyList();
},
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
CustomScrollView(
controller: _videoReplyController.scrollController,
key: const PageStorageKey<String>('评论'),
slivers: <Widget>[
const SliverToBoxAdapter(child: SizedBox(height: 12)),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
// 请求成功
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index ==
_videoReplyController.replyList.length) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.padding
.bottom),
height:
MediaQuery.of(context).padding.bottom +
100,
child: Center(
child: Obx(() => Text(
_videoReplyController.noMore.value)),
),
);
} else {
return ReplyItem(
replyItem: _videoReplyController
.replyList[index],
showReplyRow: true,
replyLevel: replyLevel);
}
},
childCount:
_videoReplyController.replyList.length + 1,
),
child: Stack(
children: [
CustomScrollView(
controller: _videoReplyController.scrollController,
key: const PageStorageKey<String>('评论'),
slivers: <Widget>[
const SliverToBoxAdapter(child: SizedBox(height: 12)),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
// 请求成功
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index ==
_videoReplyController.replyList.length) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.padding
.bottom),
height:
MediaQuery.of(context).padding.bottom +
100,
child: Center(
child: Obx(() => Text(
_videoReplyController.noMore.value)),
),
);
} else {
return ReplyItem(
replyItem:
_videoReplyController.replyList[index],
showReplyRow: true,
replyLevel: replyLevel);
}
},
childCount:
_videoReplyController.replyList.length + 1,
),
);
} else {
// 请求错误
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
);
}
),
);
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
// 请求错误
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
);
}
},
)
],
),
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 14,
right: 14,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 2),
// 评论内容为空/不足一屏
// begin: const Offset(0, 0),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: FloatingActionButton(
heroTag: null,
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (builder) {
return VideoReplyNewDialog(
replyLevel: '0',
oid: IdUtils.bv2av(Get.parameters['bvid']!),
root: 0,
parent: 0,
);
},
).then(
(value) => {
// 完成评论,数据添加
if (value != null && value['data'])
{_videoReplyController.replyList.add(value['data'])}
},
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
);
},
tooltip: '发表评论',
child: const Icon(Icons.reply),
),
}
},
)
],
),
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 14,
right: 14,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 2),
// 评论内容为空/不足一屏
// begin: const Offset(0, 0),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: FloatingActionButton(
heroTag: null,
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (builder) {
return VideoReplyNewDialog(
replyLevel: '0',
oid: IdUtils.bv2av(Get.parameters['bvid']!),
root: 0,
parent: 0,
);
},
).then(
(value) => {
// 完成评论,数据添加
if (value != null && value['data'])
{_videoReplyController.replyList.add(value['data'])}
},
);
},
tooltip: '发表评论',
child: const Icon(Icons.reply),
),
),
],
),
),
],
),
);
}

View File

@ -46,16 +46,19 @@ class ReplyItem extends StatelessWidget {
);
}
Widget lfAvtar(context) {
Widget lfAvtar(context, heroTag) {
return Container(
margin: const EdgeInsets.only(top: 5),
child: Stack(
children: [
NetworkImgLayer(
src: replyItem!.member!.avatar,
width: 34,
height: 34,
type: 'avatar',
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: replyItem!.member!.avatar,
width: 34,
height: 34,
type: 'avatar',
),
),
if (replyItem!.member!.officialVerify != null &&
replyItem!.member!.officialVerify!['type'] == 0)
@ -87,16 +90,18 @@ class ReplyItem extends StatelessWidget {
}
Widget content(context) {
String heroTag = Utils.makeHeroTag(replyItem!.mid);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// 头像、昵称
GestureDetector(
// onTap: () =>
// Get.toNamed('/member/${reply.userName}', parameters: {
// 'memberAvatar': reply.avatar,
// 'heroTag': reply.userName + heroTag,
// }),
onTap: () {
Get.toNamed('/member?mid=${replyItem!.mid}', arguments: {
'face': replyItem!.member!.avatar!,
'heroTag': heroTag
});
},
child: SizedBox(
width: double.infinity,
child: Stack(
@ -105,7 +110,7 @@ class ReplyItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
lfAvtar(context),
lfAvtar(context, heroTag),
const SizedBox(width: 12),
Text(
replyItem!.member!.uname!,
@ -367,9 +372,16 @@ class ReplyItemRow extends StatelessWidget {
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () => {
print('跳转至用户主页'),
},
..onTap = () {
String heroTag =
Utils.makeHeroTag(replies![i].member.mid);
Get.toNamed(
'/member?mid=${replies![i].member.mid}',
arguments: {
'face': replies![i].member.avatar,
'heroTag': heroTag
});
},
),
if (replies![i].isUp)
WidgetSpan(
@ -521,9 +533,13 @@ InlineSpan buildContent(BuildContext context, content) {
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () => {
print('跳转至用户主页'),
},
..onTap = () {
String heroTag = Utils.makeHeroTag(value);
Get.toNamed(
'/member?mid=$value',
arguments: {'face': '', 'heroTag': heroTag},
);
},
),
);
});