Merge branch 'design' into alpha

This commit is contained in:
guozhigq
2023-09-12 22:51:02 +08:00
9 changed files with 506 additions and 254 deletions

View File

@ -97,6 +97,9 @@ class Api {
// 操作用户关系 // 操作用户关系
static const String relationMod = '/x/relation/modify'; static const String relationMod = '/x/relation/modify';
// 相互关系查询
static const String relationSearch = '/x/space/wbi/acc/relation';
// 评论列表 // 评论列表
// https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11 // https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11
static const String replyList = '/x/v2/reply'; static const String replyList = '/x/v2/reply';

View File

@ -8,6 +8,7 @@ import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/models/user/history.dart'; import 'package:pilipala/models/user/history.dart';
import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/info.dart';
import 'package:pilipala/models/user/stat.dart'; import 'package:pilipala/models/user/stat.dart';
import 'package:pilipala/utils/wbi_sign.dart';
class UserHttp { class UserHttp {
static Future<dynamic> userStat({required int mid}) async { static Future<dynamic> userStat({required int mid}) async {
@ -248,4 +249,29 @@ class UserHttp {
return {'status': false, 'msg': res.data['message']}; return {'status': false, 'msg': res.data['message']};
} }
} }
// 相互关系查询
static Future relationSearch(int mid) async {
Map params = await WbiSign().makSign({
'mid': mid,
'token': '',
'platform': 'web',
'web_location': 1550101,
});
var res = await Request().get(
Api.relationSearch,
data: {
'mid': mid,
'w_rid': params['w_rid'],
'wts': params['wts'],
},
);
if (res.data['code'] == 0) {
// relation 主动状态
// 被动状态
return {'status': true, 'data': res.data['data']};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
} }

View File

@ -1,3 +1,4 @@
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/http/reply.dart'; import 'package:pilipala/http/reply.dart';
@ -17,6 +18,7 @@ class DynamicDetailController extends GetxController {
RxString noMore = ''.obs; RxString noMore = ''.obs;
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs; RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
RxInt acount = 0.obs; RxInt acount = 0.obs;
final ScrollController scrollController = ScrollController();
ReplySortType _sortType = ReplySortType.time; ReplySortType _sortType = ReplySortType.time;
RxString sortTypeTitle = ReplySortType.time.titles.obs; RxString sortTypeTitle = ReplySortType.time.titles.obs;

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart'; import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/http_error.dart';
@ -9,7 +10,10 @@ import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/pages/dynamics/deatil/index.dart'; import 'package:pilipala/pages/dynamics/deatil/index.dart';
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart'; import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
import 'package:pilipala/pages/video/detail/replyReply/index.dart'; import 'package:pilipala/pages/video/detail/replyReply/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/id_utils.dart';
import '../widgets/dynamic_panel.dart'; import '../widgets/dynamic_panel.dart';
@ -21,15 +25,18 @@ class DynamicDetailPage extends StatefulWidget {
State<DynamicDetailPage> createState() => _DynamicDetailPageState(); State<DynamicDetailPage> createState() => _DynamicDetailPageState();
} }
class _DynamicDetailPageState extends State<DynamicDetailPage> { class _DynamicDetailPageState extends State<DynamicDetailPage>
late DynamicDetailController? _dynamicDetailController; with TickerProviderStateMixin {
late DynamicDetailController _dynamicDetailController;
late AnimationController fabAnimationCtr;
Future? _futureBuilderFuture; Future? _futureBuilderFuture;
late StreamController<bool> titleStreamC; // appBar title late StreamController<bool> titleStreamC; // appBar title
final ScrollController scrollController = ScrollController(); late ScrollController scrollController;
bool _visibleTitle = false; bool _visibleTitle = false;
String? action; String? action;
// 回复类型 // 回复类型
late int type; late int type;
bool _isFabVisible = true;
@override @override
void initState() { void initState() {
@ -50,37 +57,30 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
} }
} catch (_) {} } catch (_) {}
} }
int commentType = Get.arguments['item'].basic!['comment_type'] ?? 11; int commentType = 11;
try {
commentType = Get.arguments['item'].basic!['comment_type'];
} catch (_) {}
type = (commentType == 0) ? 11 : commentType; type = (commentType == 0) ? 11 : commentType;
action = action =
Get.arguments.containsKey('action') ? Get.arguments['action'] : null; Get.arguments.containsKey('action') ? Get.arguments['action'] : null;
_dynamicDetailController = _dynamicDetailController =
Get.put(DynamicDetailController(oid, type), tag: oid.toString()); Get.put(DynamicDetailController(oid, type), tag: oid.toString());
_futureBuilderFuture = _dynamicDetailController!.queryReplyList(); _futureBuilderFuture = _dynamicDetailController.queryReplyList();
titleStreamC = StreamController<bool>(); titleStreamC = StreamController<bool>();
scrollController.addListener(_listen);
if (action == 'comment') { if (action == 'comment') {
_visibleTitle = true; _visibleTitle = true;
titleStreamC.add(true); titleStreamC.add(true);
} }
}
void _listen() async { fabAnimationCtr = AnimationController(
if (scrollController.position.pixels >= vsync: this,
scrollController.position.maxScrollExtent - 300) { duration: const Duration(milliseconds: 300),
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () { );
_dynamicDetailController!.queryReplyList(reqType: 'onLoad'); fabAnimationCtr.forward();
}); // 滚动事件监听
} scrollListener();
if (scrollController.offset > 55 && !_visibleTitle) {
_visibleTitle = true;
titleStreamC.add(true);
} else if (scrollController.offset <= 55 && _visibleTitle) {
_visibleTitle = false;
titleStreamC.add(false);
}
} }
void replyReply(replyItem) { void replyReply(replyItem) {
@ -107,9 +107,58 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
); );
} }
void scrollListener() {
scrollController = _dynamicDetailController.scrollController;
scrollController.addListener(
() {
// 分页加载
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
_dynamicDetailController.queryReplyList(reqType: 'onLoad');
});
}
// 标题
if (scrollController.offset > 55 && !_visibleTitle) {
_visibleTitle = true;
titleStreamC.add(true);
} else if (scrollController.offset <= 55 && _visibleTitle) {
_visibleTitle = false;
titleStreamC.add(false);
}
// fab按钮
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
_showFab();
} else if (direction == ScrollDirection.reverse) {
_hideFab();
}
},
);
}
void _showFab() {
if (!_isFabVisible) {
_isFabVisible = true;
fabAnimationCtr.forward();
}
}
void _hideFab() {
if (_isFabVisible) {
_isFabVisible = false;
fabAnimationCtr.reverse();
}
}
@override @override
void dispose() { void dispose() {
scrollController.removeListener(() {}); scrollController.removeListener(() {});
fabAnimationCtr.dispose();
scrollController.dispose();
super.dispose(); super.dispose();
} }
@ -136,155 +185,206 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
), ),
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
await _dynamicDetailController!.queryReplyList(); await _dynamicDetailController.queryReplyList();
}, },
child: CustomScrollView( child: Stack(
controller: scrollController, children: [
slivers: [ CustomScrollView(
if (action != 'comment') controller: scrollController,
SliverToBoxAdapter( slivers: [
child: DynamicPanel( if (action != 'comment')
item: _dynamicDetailController!.item, SliverToBoxAdapter(
source: 'detail', child: DynamicPanel(
), item: _dynamicDetailController.item,
), source: 'detail',
SliverPersistentHeader(
delegate: _MySliverPersistentHeaderDelegate(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
top: BorderSide(
width: 0.6,
color: Theme.of(context).dividerColor.withOpacity(0.05),
),
), ),
), ),
height: 45, SliverPersistentHeader(
padding: const EdgeInsets.only(left: 12, right: 6), delegate: _MySliverPersistentHeaderDelegate(
child: Row( child: Container(
children: [ decoration: BoxDecoration(
Obx( color: Theme.of(context).colorScheme.surface,
() => AnimatedSwitcher( border: Border(
duration: const Duration(milliseconds: 400), top: BorderSide(
transitionBuilder: width: 0.6,
(Widget child, Animation<double> animation) { color: Theme.of(context)
return ScaleTransition( .dividerColor
scale: animation, child: child); .withOpacity(0.05),
},
child: Text(
'${_dynamicDetailController!.acount.value}',
key: ValueKey<int>(
_dynamicDetailController!.acount.value),
), ),
), ),
), ),
const Text('条回复'), height: 45,
const Spacer(), padding: const EdgeInsets.only(left: 12, right: 6),
SizedBox( child: Row(
height: 35, children: [
child: TextButton.icon( Obx(
onPressed: () => () => AnimatedSwitcher(
_dynamicDetailController!.queryBySort(), duration: const Duration(milliseconds: 400),
icon: const Icon(Icons.sort, size: 16), transitionBuilder:
label: Obx(() => Text( (Widget child, Animation<double> animation) {
_dynamicDetailController!.sortTypeLabel.value, return ScaleTransition(
style: const TextStyle(fontSize: 13), scale: animation, child: child);
)), },
), child: Text(
) '${_dynamicDetailController.acount.value}',
], key: ValueKey<int>(
), _dynamicDetailController.acount.value),
),
),
pinned: true,
),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (snapshot.data['status']) {
// 请求成功
return Obx(
() => _dynamicDetailController!.replyList.isEmpty &&
_dynamicDetailController!.isLoadingMore
? SliverList(
delegate:
SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 8),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index ==
_dynamicDetailController!
.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(
_dynamicDetailController!
.noMore.value,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.outline,
),
),
),
),
);
} else {
return ReplyItem(
replyItem: _dynamicDetailController!
.replyList[index],
showReplyRow: true,
replyLevel: '1',
replyReply: (replyItem) =>
replyReply(replyItem),
replyType: ReplyType.values[type],
addReply: (replyItem) {
_dynamicDetailController!
.replyList[index].replies!
.add(replyItem);
},
);
}
},
childCount:
_dynamicDetailController!.replyList.length +
1,
), ),
), ),
),
const Text('条回复'),
const Spacer(),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: () =>
_dynamicDetailController.queryBySort(),
icon: const Icon(Icons.sort, size: 16),
label: Obx(() => Text(
_dynamicDetailController
.sortTypeLabel.value,
style: const TextStyle(fontSize: 13),
)),
),
)
],
),
),
),
pinned: true,
),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (snapshot.data['status']) {
// 请求成功
return Obx(
() => _dynamicDetailController.replyList.isEmpty &&
_dynamicDetailController.isLoadingMore
? SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return const VideoReplySkeleton();
}, childCount: 8),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index ==
_dynamicDetailController
.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(
_dynamicDetailController
.noMore.value,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.outline,
),
),
),
),
);
} else {
return ReplyItem(
replyItem: _dynamicDetailController
.replyList[index],
showReplyRow: true,
replyLevel: '1',
replyReply: (replyItem) =>
replyReply(replyItem),
replyType: ReplyType.values[type],
addReply: (replyItem) {
_dynamicDetailController
.replyList[index].replies!
.add(replyItem);
},
);
}
},
childCount: _dynamicDetailController
.replyList.length +
1,
),
),
);
} else {
// 请求错误
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 8),
);
}
},
)
],
),
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 14,
right: 14,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 2),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: FloatingActionButton(
heroTag: null,
onPressed: () {
feedBack();
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return VideoReplyNewDialog(
oid: _dynamicDetailController.oid ??
IdUtils.bv2av(Get.parameters['bvid']!),
root: 0,
parent: 0,
replyType: ReplyType.values[type],
);
},
).then(
(value) => {
// 完成评论,数据添加
if (value != null && value['data'] != null)
{
_dynamicDetailController.replyList
.add(value['data']),
_dynamicDetailController.acount.value++
}
},
); );
} else { },
// 请求错误 tooltip: '评论动态',
return HttpError( child: const Icon(Icons.reply),
errMsg: data['msg'], ),
fn: () => setState(() {}), ),
); ),
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 8),
);
}
},
)
], ],
), ),
), ),

View File

@ -3,10 +3,12 @@ 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/member.dart'; import 'package:pilipala/http/member.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/http/video.dart'; import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/member/archive.dart'; import 'package:pilipala/models/member/archive.dart';
import 'package:pilipala/models/member/info.dart'; import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:share_plus/share_plus.dart';
class MemberController extends GetxController { class MemberController extends GetxController {
late int mid; late int mid;
@ -19,6 +21,8 @@ class MemberController extends GetxController {
// 投稿列表 // 投稿列表
RxList<VListItemModel>? archiveList = [VListItemModel()].obs; RxList<VListItemModel>? archiveList = [VListItemModel()].obs;
var userInfo; var userInfo;
RxInt attribute = (-1).obs;
RxString attributeText = '关注'.obs;
@override @override
void onInit() { void onInit() {
@ -28,6 +32,7 @@ class MemberController extends GetxController {
ownerMid = userInfo != null ? userInfo.mid : -1; ownerMid = userInfo != null ? userInfo.mid : -1;
face = Get.arguments['face'] ?? ''; face = Get.arguments['face'] ?? '';
heroTag = Get.arguments['heroTag'] ?? ''; heroTag = Get.arguments['heroTag'] ?? '';
relationSearch();
} }
// 获取用户信息 // 获取用户信息
@ -63,7 +68,10 @@ class MemberController extends GetxController {
SmartDialog.showToast('账号未登录'); SmartDialog.showToast('账号未登录');
return; return;
} }
if (attribute.value == 128) {
blockUser();
return;
}
SmartDialog.show( SmartDialog.show(
useSystem: true, useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide, animationType: SmartAnimationType.centerFade_otherSlide,
@ -73,8 +81,12 @@ class MemberController extends GetxController {
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'), content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => SmartDialog.dismiss(), onPressed: () => SmartDialog.dismiss(),
child: const Text('点错了')), child: Text(
'点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton( TextButton(
onPressed: () async { onPressed: () async {
await VideoHttp.relationMod( await VideoHttp.relationMod(
@ -83,8 +95,7 @@ class MemberController extends GetxController {
reSrc: 11, reSrc: 11,
); );
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!; memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
SmartDialog.dismiss(); relationSearch();
SmartDialog.showLoading();
SmartDialog.dismiss(); SmartDialog.dismiss();
memberInfo.update((val) {}); memberInfo.update((val) {});
}, },
@ -95,4 +106,69 @@ class MemberController extends GetxController {
}, },
); );
} }
// 关系查询
Future relationSearch() async {
if (userInfo == null) return;
var res = await UserHttp.relationSearch(mid);
if (res['status']) {
attribute.value = res['data']['relation']['attribute'];
attributeText.value = attribute.value == 0
? '关注'
: attribute.value == 2
? '已关注'
: attribute.value == 2
? '已互粉'
: '已拉黑';
}
}
// 拉黑用户
Future blockUser() async {
if (userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
var res = await VideoHttp.relationMod(
mid: mid,
act: attribute.value != 128 ? 5 : 6,
reSrc: 11,
);
SmartDialog.dismiss();
if (res['status']) {
attribute.value = attribute.value != 128 ? 128 : 0;
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
memberInfo.value.isFollowed = false;
relationSearch();
memberInfo.update((val) {});
}
},
child: const Text('确认'),
)
],
);
},
);
}
void shareUser() {
Share.share('${memberInfo.value.name} - https://space.bilibili.com/$mid');
}
} }

View File

@ -102,7 +102,35 @@ class _MemberPageState extends State<MemberPage>
}, },
), ),
actions: [ actions: [
IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert)), PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
PopupMenuItem(
onTap: () => _memberController.blockUser(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.block, size: 19),
const SizedBox(width: 10),
Text(_memberController.attribute.value != 128
? '加入黑名单'
: '移除黑名单'),
],
),
),
PopupMenuItem(
onTap: () => _memberController.shareUser(),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.share_outlined, size: 19),
SizedBox(width: 10),
Text('分享UP主'),
],
),
),
],
),
const SizedBox(width: 4), const SizedBox(width: 4),
], ],
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(

View File

@ -15,62 +15,63 @@ Widget profile(ctr, {loadingStatus = false}) {
child: Row( child: Row(
children: [ children: [
Hero( Hero(
tag: ctr.heroTag!, tag: ctr.heroTag!,
child: Stack( child: Stack(
children: [ children: [
NetworkImgLayer( NetworkImgLayer(
width: 90, width: 90,
height: 90, height: 90,
type: 'avatar', type: 'avatar',
src: !loadingStatus ? memberInfo.face : ctr.face, src: !loadingStatus ? memberInfo.face : ctr.face,
), ),
if (!loadingStatus && if (!loadingStatus &&
memberInfo.liveRoom != null && memberInfo.liveRoom != null &&
memberInfo.liveRoom!.liveStatus == 1) memberInfo.liveRoom!.liveStatus == 1)
Positioned( Positioned(
bottom: 0, bottom: 0,
left: 14, left: 14,
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
LiveItemModel liveItem = LiveItemModel.fromJson({ LiveItemModel liveItem = LiveItemModel.fromJson({
'title': memberInfo.liveRoom!.title, 'title': memberInfo.liveRoom!.title,
'uname': memberInfo.name, 'uname': memberInfo.name,
'face': memberInfo.face, 'face': memberInfo.face,
'roomid': memberInfo.liveRoom!.roomId, 'roomid': memberInfo.liveRoom!.roomId,
'watched_show': memberInfo.liveRoom!.watchedShow, 'watched_show': memberInfo.liveRoom!.watchedShow,
}); });
Get.toNamed( Get.toNamed(
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
arguments: {'liveItem': liveItem}, arguments: {'liveItem': liveItem},
); );
}, },
child: Container( child: Container(
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2), padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
borderRadius: borderRadius:
const BorderRadius.all(Radius.circular(10)), 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),
)
]),
), ),
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), const SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
@ -152,34 +153,41 @@ Widget profile(ctr, {loadingStatus = false}) {
if (ctr.ownerMid != ctr.mid) ...[ if (ctr.ownerMid != ctr.mid) ...[
Row( Row(
children: [ children: [
TextButton( Obx(
onPressed: () => ctr.actionRelationMod(), () => Expanded(
style: TextButton.styleFrom( child: TextButton(
padding: const EdgeInsets.only(left: 42, right: 42), onPressed: () => ctr.actionRelationMod(),
foregroundColor: style: TextButton.styleFrom(
!loadingStatus && memberInfo.isFollowed! foregroundColor: ctr.attribute.value == -1
? Theme.of(context).colorScheme.outline ? Colors.transparent
: Theme.of(context).colorScheme.onPrimary, : ctr.attribute.value != 0
backgroundColor: !loadingStatus && ? Theme.of(context).colorScheme.outline
memberInfo.isFollowed! : Theme.of(context)
? Theme.of(context).colorScheme.onInverseSurface .colorScheme
: Theme.of(context) .onPrimary,
.colorScheme backgroundColor: ctr.attribute.value != 0
.primary, // 设置按钮背景色 ? Theme.of(context)
.colorScheme
.onInverseSurface
: Theme.of(context)
.colorScheme
.primary, // 设置按钮背景色
),
child: Obx(() => Text(ctr.attributeText.value)),
),
), ),
child: Text(!loadingStatus && memberInfo.isFollowed!
? '取关'
: '关注'),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
TextButton( Expanded(
onPressed: () {}, child: TextButton(
style: TextButton.styleFrom( onPressed: () {},
padding: const EdgeInsets.only(left: 42, right: 42), style: TextButton.styleFrom(
backgroundColor: backgroundColor: Theme.of(context)
Theme.of(context).colorScheme.onInverseSurface, .colorScheme
.onInverseSurface,
),
child: const Text('发消息'),
), ),
child: const Text('发消息'),
) )
], ],
) )

View File

@ -62,6 +62,12 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
vsync: this, duration: const Duration(milliseconds: 300)); vsync: this, duration: const Duration(milliseconds: 300));
_futureBuilderFuture = _videoReplyController.queryReplyList(); _futureBuilderFuture = _videoReplyController.queryReplyList();
fabAnimationCtr.forward();
scrollListener();
}
void scrollListener() {
scrollController = _videoReplyController.scrollController; scrollController = _videoReplyController.scrollController;
scrollController.addListener( scrollController.addListener(
() { () {
@ -81,7 +87,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
} }
}, },
); );
fabAnimationCtr.forward();
} }
void _showFab() { void _showFab() {
@ -112,9 +117,10 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
@override @override
void dispose() { void dispose() {
super.dispose(); scrollController.removeListener(() {});
fabAnimationCtr.dispose(); fabAnimationCtr.dispose();
scrollController.dispose(); scrollController.dispose();
super.dispose();
} }
@override @override
@ -128,7 +134,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
child: Stack( child: Stack(
children: [ children: [
CustomScrollView( CustomScrollView(
controller: _videoReplyController.scrollController, controller: scrollController,
key: const PageStorageKey<String>('评论'), key: const PageStorageKey<String>('评论'),
slivers: <Widget>[ slivers: <Widget>[
SliverPersistentHeader( SliverPersistentHeader(

View File

@ -744,11 +744,14 @@ InlineSpan buildContent(
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
// 跳转到指定位置 // 跳转到指定位置
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']) try {
.plPlayerController Get.find<VideoDetailController>(
.seekTo( tag: Get.arguments['heroTag'])
Duration(seconds: Utils.duration(matchStr)), .plPlayerController
); .seekTo(
Duration(seconds: Utils.duration(matchStr)),
);
} catch (_) {}
}, },
), ),
); );