diff --git a/lib/http/api.dart b/lib/http/api.dart index 0a2e6616..ef9fe94a 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -97,6 +97,9 @@ class Api { // 操作用户关系 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 static const String replyList = '/x/v2/reply'; diff --git a/lib/http/user.dart b/lib/http/user.dart index 44002805..b27cfde1 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -8,6 +8,7 @@ import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/history.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; +import 'package:pilipala/utils/wbi_sign.dart'; class UserHttp { static Future userStat({required int mid}) async { @@ -248,4 +249,29 @@ class UserHttp { 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']}; + } + } } diff --git a/lib/pages/dynamics/deatil/controller.dart b/lib/pages/dynamics/deatil/controller.dart index 96ab65a6..62f0245d 100644 --- a/lib/pages/dynamics/deatil/controller.dart +++ b/lib/pages/dynamics/deatil/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/reply.dart'; @@ -17,6 +18,7 @@ class DynamicDetailController extends GetxController { RxString noMore = ''.obs; RxList replyList = [ReplyItemModel()].obs; RxInt acount = 0.obs; + final ScrollController scrollController = ScrollController(); ReplySortType _sortType = ReplySortType.time; RxString sortTypeTitle = ReplySortType.time.titles.obs; diff --git a/lib/pages/dynamics/deatil/view.dart b/lib/pages/dynamics/deatil/view.dart index b17eb78b..4c49ecc7 100644 --- a/lib/pages/dynamics/deatil/view.dart +++ b/lib/pages/dynamics/deatil/view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/skeleton/video_reply.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/widgets/author_panel.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/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import '../widgets/dynamic_panel.dart'; @@ -21,15 +25,18 @@ class DynamicDetailPage extends StatefulWidget { State createState() => _DynamicDetailPageState(); } -class _DynamicDetailPageState extends State { - late DynamicDetailController? _dynamicDetailController; +class _DynamicDetailPageState extends State + with TickerProviderStateMixin { + late DynamicDetailController _dynamicDetailController; + late AnimationController fabAnimationCtr; Future? _futureBuilderFuture; late StreamController titleStreamC; // appBar title - final ScrollController scrollController = ScrollController(); + late ScrollController scrollController; bool _visibleTitle = false; String? action; // 回复类型 late int type; + bool _isFabVisible = true; @override void initState() { @@ -50,37 +57,30 @@ class _DynamicDetailPageState extends State { } } 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; action = Get.arguments.containsKey('action') ? Get.arguments['action'] : null; _dynamicDetailController = Get.put(DynamicDetailController(oid, type), tag: oid.toString()); - _futureBuilderFuture = _dynamicDetailController!.queryReplyList(); + _futureBuilderFuture = _dynamicDetailController.queryReplyList(); titleStreamC = StreamController(); - scrollController.addListener(_listen); if (action == 'comment') { _visibleTitle = true; titleStreamC.add(true); } - } - void _listen() async { - 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); - } + fabAnimationCtr = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + ); + fabAnimationCtr.forward(); + // 滚动事件监听 + scrollListener(); } void replyReply(replyItem) { @@ -107,9 +107,58 @@ class _DynamicDetailPageState extends State { ); } + 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 void dispose() { scrollController.removeListener(() {}); + fabAnimationCtr.dispose(); + scrollController.dispose(); super.dispose(); } @@ -136,155 +185,206 @@ class _DynamicDetailPageState extends State { ), body: RefreshIndicator( onRefresh: () async { - await _dynamicDetailController!.queryReplyList(); + await _dynamicDetailController.queryReplyList(); }, - child: CustomScrollView( - controller: scrollController, - slivers: [ - if (action != 'comment') - SliverToBoxAdapter( - 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), - ), + child: Stack( + children: [ + CustomScrollView( + controller: scrollController, + slivers: [ + if (action != 'comment') + SliverToBoxAdapter( + child: DynamicPanel( + item: _dynamicDetailController.item, + source: 'detail', ), ), - height: 45, - padding: const EdgeInsets.only(left: 12, right: 6), - child: Row( - children: [ - Obx( - () => AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - '${_dynamicDetailController!.acount.value}', - key: ValueKey( - _dynamicDetailController!.acount.value), + 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), ), ), ), - 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, + height: 45, + padding: const EdgeInsets.only(left: 12, right: 6), + child: Row( + children: [ + Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Text( + '${_dynamicDetailController.acount.value}', + key: ValueKey( + _dynamicDetailController.acount.value), ), ), + ), + 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( + 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 { - // 请求错误 - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); - } - } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ); - } - }, - ) + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ), + ), + ), ], ), ), diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index db4deaae..df1ecf43 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -3,10 +3,12 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/member.dart'; +import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/member/archive.dart'; import 'package:pilipala/models/member/info.dart'; import 'package:pilipala/utils/storage.dart'; +import 'package:share_plus/share_plus.dart'; class MemberController extends GetxController { late int mid; @@ -19,6 +21,8 @@ class MemberController extends GetxController { // 投稿列表 RxList? archiveList = [VListItemModel()].obs; var userInfo; + RxInt attribute = (-1).obs; + RxString attributeText = '关注'.obs; @override void onInit() { @@ -28,6 +32,7 @@ class MemberController extends GetxController { ownerMid = userInfo != null ? userInfo.mid : -1; face = Get.arguments['face'] ?? ''; heroTag = Get.arguments['heroTag'] ?? ''; + relationSearch(); } // 获取用户信息 @@ -63,7 +68,10 @@ class MemberController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - + if (attribute.value == 128) { + blockUser(); + return; + } SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, @@ -73,8 +81,12 @@ class MemberController extends GetxController { content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'), actions: [ TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('点错了')), + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), TextButton( onPressed: () async { await VideoHttp.relationMod( @@ -83,8 +95,7 @@ class MemberController extends GetxController { reSrc: 11, ); memberInfo.value.isFollowed = !memberInfo.value.isFollowed!; - SmartDialog.dismiss(); - SmartDialog.showLoading(); + relationSearch(); SmartDialog.dismiss(); 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'); + } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 55dad4f0..c5130206 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -102,7 +102,35 @@ class _MemberPageState extends State }, ), actions: [ - IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert)), + PopupMenuButton( + icon: const Icon(Icons.more_vert), + itemBuilder: (BuildContext context) => [ + 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), ], flexibleSpace: FlexibleSpaceBar( diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index d8dc651a..2574c9ae 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -15,62 +15,63 @@ Widget profile(ctr, {loadingStatus = false}) { child: Row( children: [ Hero( - tag: ctr.heroTag!, - child: Stack( - children: [ - NetworkImgLayer( - width: 90, - height: 90, - type: 'avatar', - src: !loadingStatus ? memberInfo.face : ctr.face, - ), - if (!loadingStatus && - memberInfo.liveRoom != null && - memberInfo.liveRoom!.liveStatus == 1) - Positioned( - bottom: 0, - left: 14, - child: GestureDetector( - onTap: () { - LiveItemModel liveItem = LiveItemModel.fromJson({ - 'title': memberInfo.liveRoom!.title, - 'uname': memberInfo.name, - 'face': memberInfo.face, - 'roomid': memberInfo.liveRoom!.roomId, - 'watched_show': memberInfo.liveRoom!.watchedShow, - }); - Get.toNamed( - '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', - arguments: {'liveItem': liveItem}, - ); - }, - 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), - ) - ]), + tag: ctr.heroTag!, + child: Stack( + children: [ + NetworkImgLayer( + width: 90, + height: 90, + type: 'avatar', + src: !loadingStatus ? memberInfo.face : ctr.face, + ), + if (!loadingStatus && + memberInfo.liveRoom != null && + memberInfo.liveRoom!.liveStatus == 1) + Positioned( + bottom: 0, + left: 14, + child: GestureDetector( + onTap: () { + LiveItemModel liveItem = LiveItemModel.fromJson({ + 'title': memberInfo.liveRoom!.title, + 'uname': memberInfo.name, + 'face': memberInfo.face, + 'roomid': memberInfo.liveRoom!.roomId, + 'watched_show': memberInfo.liveRoom!.watchedShow, + }); + Get.toNamed( + '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', + arguments: {'liveItem': liveItem}, + ); + }, + 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( @@ -152,34 +153,41 @@ Widget profile(ctr, {loadingStatus = false}) { if (ctr.ownerMid != ctr.mid) ...[ Row( children: [ - TextButton( - onPressed: () => ctr.actionRelationMod(), - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 42, right: 42), - foregroundColor: - !loadingStatus && memberInfo.isFollowed! - ? Theme.of(context).colorScheme.outline - : Theme.of(context).colorScheme.onPrimary, - backgroundColor: !loadingStatus && - memberInfo.isFollowed! - ? Theme.of(context).colorScheme.onInverseSurface - : Theme.of(context) - .colorScheme - .primary, // 设置按钮背景色 + Obx( + () => Expanded( + child: TextButton( + onPressed: () => ctr.actionRelationMod(), + style: TextButton.styleFrom( + foregroundColor: ctr.attribute.value == -1 + ? Colors.transparent + : ctr.attribute.value != 0 + ? Theme.of(context).colorScheme.outline + : Theme.of(context) + .colorScheme + .onPrimary, + backgroundColor: ctr.attribute.value != 0 + ? 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), - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 42, right: 42), - backgroundColor: - Theme.of(context).colorScheme.onInverseSurface, + Expanded( + child: TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + backgroundColor: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + child: const Text('发消息'), ), - child: const Text('发消息'), ) ], ) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 7b960014..d5469d92 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -62,6 +62,12 @@ class _VideoReplyPanelState extends State vsync: this, duration: const Duration(milliseconds: 300)); _futureBuilderFuture = _videoReplyController.queryReplyList(); + + fabAnimationCtr.forward(); + scrollListener(); + } + + void scrollListener() { scrollController = _videoReplyController.scrollController; scrollController.addListener( () { @@ -81,7 +87,6 @@ class _VideoReplyPanelState extends State } }, ); - fabAnimationCtr.forward(); } void _showFab() { @@ -112,9 +117,10 @@ class _VideoReplyPanelState extends State @override void dispose() { - super.dispose(); + scrollController.removeListener(() {}); fabAnimationCtr.dispose(); scrollController.dispose(); + super.dispose(); } @override @@ -128,7 +134,7 @@ class _VideoReplyPanelState extends State child: Stack( children: [ CustomScrollView( - controller: _videoReplyController.scrollController, + controller: scrollController, key: const PageStorageKey('评论'), slivers: [ SliverPersistentHeader( diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index fb06ca0a..76e63e72 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -744,11 +744,14 @@ InlineSpan buildContent( recognizer: TapGestureRecognizer() ..onTap = () { // 跳转到指定位置 - Get.find(tag: Get.arguments['heroTag']) - .plPlayerController - .seekTo( - Duration(seconds: Utils.duration(matchStr)), - ); + try { + Get.find( + tag: Get.arguments['heroTag']) + .plPlayerController + .seekTo( + Duration(seconds: Utils.duration(matchStr)), + ); + } catch (_) {} }, ), );