From ed60c274fc0423f1069a4e2c365b72d718e455be Mon Sep 17 00:00:00 2001 From: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:24:39 +0800 Subject: [PATCH] refa: avatar (#722) * mod: unify icon * refa: avatar --- lib/common/widgets/avatar.dart | 172 ++++++++++++ lib/pages/dynamics/widgets/author_panel.dart | 66 ++--- lib/pages/dynamics/widgets/dynamic_panel.dart | 69 ++--- lib/pages/member/widget/user_info_card.dart | 218 ++++------------ lib/pages/search_panel/user/widgets/item.dart | 37 +-- lib/pages/video/detail/introduction/view.dart | 67 ++--- .../detail/reply/widgets/reply_item_grpc.dart | 244 ++++++------------ 7 files changed, 375 insertions(+), 498 deletions(-) create mode 100644 lib/common/widgets/avatar.dart diff --git a/lib/common/widgets/avatar.dart b/lib/common/widgets/avatar.dart new file mode 100644 index 00000000..ef7c2d96 --- /dev/null +++ b/lib/common/widgets/avatar.dart @@ -0,0 +1,172 @@ +import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'network_img_layer.dart'; + +class Avatar extends StatelessWidget { + final _BadgeType _badgeType; + final String avatar; + final double size; + final String? garbPendantImage; + final dynamic roomId; + final VoidCallback? onTap; + + const Avatar({ + super.key, + required this.avatar, + this.size = 80, + bool? isVip, + int? officialType, + this.garbPendantImage, + this.roomId, + this.onTap, + }) : _badgeType = officialType == null || officialType < 0 + ? isVip == true + ? _BadgeType.vip + : _BadgeType.none + : officialType == 0 + ? _BadgeType.person + : _BadgeType.institution; + + static final showDynDecorate = GStorage.showDynDecorate; + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Stack( + alignment: Alignment.bottomCenter, + clipBehavior: Clip.none, + children: [ + onTap == null + ? _buildAvatar(colorScheme) + : GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: _buildAvatar(colorScheme), + ), + if (!garbPendantImage.isNullOrEmpty) + Positioned( + top: -size * 0.375, // -(size * 1.75 - size) / 2 + child: IgnorePointer( + child: CachedNetworkImage( + width: size * 1.75, + height: size * 1.75, + imageUrl: Utils.thumbnailImgUrl(garbPendantImage), + ), + ), + ), + if (roomId != null) + Positioned( + bottom: 0, + child: InkWell( + onTap: () { + Get.toNamed('/liveRoom?roomid=$roomId'); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: size / 12), + decoration: BoxDecoration( + color: colorScheme.secondaryContainer, + borderRadius: BorderRadius.circular(size / 6), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.equalizer_rounded, + size: size / 3, + color: colorScheme.onSecondaryContainer, + ), + Text( + '直播中', + style: TextStyle( + height: 0.5, + fontSize: size / 6, + color: colorScheme.onSecondaryContainer, + ), + ), + ], + ), + ), + ), + ) + else if (_badgeType != _BadgeType.none) + _buildBadge(context, colorScheme), + ], + ); + } + + Widget _buildAvatar(ColorScheme colorScheme) => DecoratedBox( + decoration: BoxDecoration( + border: Border.all( + width: size / 32, + color: colorScheme.surface, + ), + shape: BoxShape.circle, + ), + child: NetworkImgLayer( + src: avatar, + width: size, + height: size, + type: 'avatar', + )); + + Widget _buildBadge(BuildContext context, ColorScheme colorScheme) { + final child = switch (_badgeType) { + _BadgeType.vip => Container( + padding: EdgeInsets.all(size / 32), + decoration: BoxDecoration( + border: Border.all( + width: size / 32, + color: colorScheme.surface, + ), + shape: BoxShape.circle, + // color: _badgeType.color, + color: context.vipColor, + ), + child: Text( + '大', + style: TextStyle( + // backgroundColor: Color(0xFFFF6699), + height: 1.1, + fontWeight: FontWeight.w900, + color: colorScheme.surface, + fontSize: size / 5, + ), + ), + ), + _ => DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorScheme.surface, + ), + child: Icon( + Icons.offline_bolt, + color: _badgeType.color, + size: size / 3, + semanticLabel: _badgeType.desc, + ), + ), + }; + return Positioned( + right: 0, + bottom: 0, + child: IgnorePointer(child: child), + ); + } +} + +enum _BadgeType { none, vip, person, institution } + +extension _BadgeTypeExt on _BadgeType { + String get desc => const ['', '大会员', '认证个人', '认证机构'][index]; + Color get color => const [ + Colors.transparent, + Color(0xFFFF6699), + Colors.yellow, + Colors.lightBlueAccent + ][index]; +} diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 40699cf8..ed13bf8b 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:PiliPlus/common/widgets/avatar.dart'; import 'package:PiliPlus/common/widgets/report.dart'; import 'package:PiliPlus/common/widgets/save_panel.dart'; import 'package:PiliPlus/http/index.dart'; @@ -12,7 +13,6 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:PiliPlus/common/widgets/network_img_layer.dart'; import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -38,15 +38,29 @@ class AuthorPanel extends StatelessWidget { this.onSetTop, }); - Widget _buildAvatar(double size) => NetworkImgLayer( - width: size, - height: size, - type: 'avatar', - src: item.modules.moduleAuthor.face, + Widget _buildAvatar() => Avatar( + avatar: item.modules.moduleAuthor.face, + size: 34, + isVip: (item.modules.moduleAuthor?.vip?['status'] ?? 0) > 0, + officialType: null, // 已被注释 + garbPendantImage: item.modules.moduleAuthor?.pendant?['image'], + onTap: (item.modules.moduleAuthor.type == 'AUTHOR_TYPE_PGC' || + item.modules.moduleAuthor.type == 'AUTHOR_TYPE_UGC_SEASON') + ? null // 番剧 + : () { + feedBack(); + Get.toNamed( + '/member?mid=${item.modules.moduleAuthor.mid}', + arguments: { + 'face': item.modules.moduleAuthor.face, + }, + ); + }, ); @override Widget build(BuildContext context) { + final theme = Theme.of(context); String? pubTime = item.modules.moduleAuthor.pubTs != null ? isSave ? DateTime.fromMillisecondsSinceEpoch( @@ -63,31 +77,7 @@ class AuthorPanel extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - GestureDetector( - onTap: () { - // 番剧 - if (item.modules.moduleAuthor.type == 'AUTHOR_TYPE_PGC' || - item.modules.moduleAuthor.type == - 'AUTHOR_TYPE_UGC_SEASON') { - return; - } - feedBack(); - Get.toNamed( - '/member?mid=${item.modules.moduleAuthor.mid}', - arguments: { - 'face': item.modules.moduleAuthor.face, - }, - ); - }, - child: (item.modules.moduleAuthor?.pendant?['image'] as String?) - ?.isNotEmpty == - true - ? Padding( - padding: const EdgeInsets.all(3), - child: _buildAvatar(34), - ) - : _buildAvatar(40), - ), + _buildAvatar(), const SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -100,18 +90,16 @@ class AuthorPanel extends StatelessWidget { item.modules.moduleAuthor!.vip['status'] > 0 && item.modules.moduleAuthor!.vip['type'] == 2 ? context.vipColor - : Theme.of(context).colorScheme.onSurface, - fontSize: - Theme.of(context).textTheme.titleSmall!.fontSize, + : theme.colorScheme.onSurface, + fontSize: theme.textTheme.titleSmall!.fontSize, ), ), if (pubTime != null) Text( '$pubTime${item.modules.moduleAuthor.pubAction != null ? ' ${item.modules.moduleAuthor.pubAction}' : ''}', style: TextStyle( - color: Theme.of(context).colorScheme.outline, - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, + color: theme.colorScheme.outline, + fontSize: theme.textTheme.labelSmall!.fontSize, ), ), ], @@ -133,7 +121,7 @@ class AuthorPanel extends StatelessWidget { const BorderRadius.all(Radius.circular(4)), border: Border.all( width: 1.25, - color: Theme.of(context).colorScheme.primary, + color: theme.colorScheme.primary, ), ), child: Text( @@ -141,7 +129,7 @@ class AuthorPanel extends StatelessWidget { style: TextStyle( height: 1, fontSize: 12, - color: Theme.of(context).colorScheme.primary, + color: theme.colorScheme.primary, ), strutStyle: const StrutStyle( leading: 0, diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index cf82019f..901bd428 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,7 +1,6 @@ import 'package:PiliPlus/common/widgets/image_save.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'action_panel.dart'; @@ -99,53 +98,31 @@ class DynamicPanel extends StatelessWidget { ); } }, - child: (item.modules.moduleAuthor?.pendant?['image'] as String?) - ?.isNotEmpty == - true - ? Stack( - clipBehavior: Clip.none, - children: [ - _buildContent(context, item, source, callback), - Positioned( - left: 2, - top: 2, - child: IgnorePointer( - child: CachedNetworkImage( - width: 60, - height: 60, - imageUrl: item.modules.moduleAuthor.pendant['image'], - ), - ), - ), - ], - ) - : _buildContent(context, item, source, callback), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 6), + child: AuthorPanel( + item: item, + source: source, + onRemove: onRemove, + isSave: isSave, + onSetTop: onSetTop, + ), + ), + if (item!.modules!.moduleDynamic!.desc != null || + item!.modules!.moduleDynamic!.major != null) + content(isSave, context, item, source, callback), + forWard(isSave, item, context, source, callback), + const SizedBox(height: 2), + if (source == null) ActionPanel(item: item), + if (source == 'detail' && !isSave) const SizedBox(height: 12), + ], + ), ), ), ); } - - Widget _buildContent(context, item, source, callback) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(12, 12, 12, 6), - child: AuthorPanel( - item: item, - source: source, - onRemove: onRemove, - isSave: isSave, - onSetTop: onSetTop, - ), - ), - if (item!.modules!.moduleDynamic!.desc != null || - item!.modules!.moduleDynamic!.major != null) - content(isSave, context, item, source, callback), - forWard(isSave, item, context, source, callback), - const SizedBox(height: 2), - if (source == null) ActionPanel(item: item), - if (source == 'detail' && !isSave) const SizedBox(height: 12), - ], - ); } diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index e9b64892..3f359a6a 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -1,7 +1,6 @@ +import 'package:PiliPlus/common/widgets/avatar.dart'; import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart' show SourceModel; -import 'package:PiliPlus/common/widgets/network_img_layer.dart'; -import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/space/card.dart' as space; import 'package:PiliPlus/models/space/images.dart' as space; import 'package:PiliPlus/utils/extension.dart'; @@ -72,24 +71,24 @@ class UserInfoCard extends StatelessWidget { _buildHeader(BuildContext context) { bool darken = Theme.of(context).brightness == Brightness.dark; - String? imgUrl = darken - ? (images.nightImgurl?.isEmpty == true - ? images.imgUrl?.http2https - : images.nightImgurl?.http2https) - : images.imgUrl?.http2https; + String imgUrl = Utils.thumbnailImgUrl(darken + ? images.nightImgurl?.isEmpty == true + ? images.imgUrl + : images.nightImgurl + : images.imgUrl); return Hero( - tag: imgUrl ?? '', + tag: imgUrl, child: GestureDetector( onTap: () { context.imageView( - imgList: [SourceModel(url: imgUrl ?? '')], + imgList: [SourceModel(url: imgUrl)], ); }, child: CachedNetworkImage( - imageUrl: imgUrl?.http2https ?? '', + imageUrl: imgUrl, width: double.infinity, height: 135, - imageBuilder: (context, imageProvider) => Container( + imageBuilder: (context, imageProvider) => DecoratedBox( decoration: BoxDecoration( image: DecorationImage( image: imageProvider, @@ -135,15 +134,24 @@ class UserInfoCard extends StatelessWidget { ), if (card.vip?.vipStatus == 1) ...[ const SizedBox(width: 8), - Image.network( - card.vip!.label!.image!.http2https, - height: 20, + Container( + padding: const EdgeInsets.symmetric(horizontal: 5), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: context.vipColor), + child: Text( + card.vip!.label!.text!, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 10, + color: Theme.of(context).colorScheme.surface), + ), ), ], - if (card.nameplate?.image?.isNotEmpty == true) ...[ + if (card.nameplate?.imageSmall?.isNotEmpty == true) ...[ const SizedBox(width: 8), - Image.network( - card.nameplate!.image!.http2https, + CachedNetworkImage( + imageUrl: Utils.thumbnailImgUrl(card.nameplate!.imageSmall), height: 20, ), ], @@ -180,7 +188,7 @@ class UserInfoCard extends StatelessWidget { if (card.officialVerify?.desc?.isNotEmpty == true) Container( margin: const EdgeInsets.only(left: 20, top: 8, right: 20), - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Theme.of(context).colorScheme.onInverseSurface, @@ -191,16 +199,12 @@ class UserInfoCard extends StatelessWidget { if (card.officialVerify?.icon?.isNotEmpty == true) ...[ WidgetSpan( alignment: PlaceholderAlignment.middle, - child: DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surface, - ), - child: CachedNetworkImage( - width: 18, - height: 18, - imageUrl: card.officialVerify!.icon!.http2https, - ), + child: Icon( + Icons.offline_bolt, + color: card.officialVerify?.type == 0 + ? Colors.yellow + : Colors.lightBlueAccent, + size: 18, ), ), TextSpan( @@ -227,9 +231,7 @@ class UserInfoCard extends StatelessWidget { padding: const EdgeInsets.only(left: 20, top: 6, right: 20), child: SelectableText( card.sign!.trim().replaceAll(RegExp(r'\n{2,}'), '\n'), - style: const TextStyle( - fontSize: 14, - ), + style: const TextStyle(fontSize: 14), ), ), Padding( @@ -411,8 +413,7 @@ class UserInfoCard extends StatelessWidget { 0 => '关注', 1 => '悄悄关注', 2 => '已关注', - 4 => '互相关注', - 6 => '已互关', + 4 || 6 => '已互关', 128 => '移除黑名单', -10 => '特别关注', // 该状态码并不是官方状态码 _ => relation.toString(), @@ -428,51 +429,20 @@ class UserInfoCard extends StatelessWidget { ], ); - _buildBadge(BuildContext context) => IgnorePointer( - child: DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surface, - ), - child: card.officialVerify?.icon?.isNotEmpty == true - ? CachedNetworkImage( - imageUrl: card.officialVerify!.icon!.http2https, - width: 22, - height: 22, - ) - : Image.asset( - 'assets/images/big-vip.png', - width: 22, - height: 22, - ), - ), - ); - _buildAvatar(BuildContext context) => Hero( - tag: card.face ?? '', - child: GestureDetector( - onTap: () { - context.imageView( - imgList: [SourceModel(url: card.face ?? '')], - ); - }, - child: Container( - decoration: BoxDecoration( - border: Border.all( - width: 2.5, - color: Theme.of(context).colorScheme.surface, - ), - shape: BoxShape.circle, - ), - child: NetworkImgLayer( - src: card.face, - type: 'avatar', - width: 80, - height: 80, - ), - ), - ), - ); + tag: card.face ?? '', + child: Avatar( + avatar: card.face ?? '', + size: 80, + officialType: card.officialVerify?.type, + isVip: (card.vip?.vipStatus ?? -1) > 0, + garbPendantImage: card.pendant!.image!, + roomId: live is Map && ((live['liveStatus'] as int?) ?? 0) == 1 + ? live['roomid'] + : null, + onTap: () => context + .imageView(imgList: [SourceModel(url: card.face.http2https)]), + )); _buildV(BuildContext context) => Column( mainAxisSize: MainAxisSize.min, @@ -493,32 +463,6 @@ class UserInfoCard extends StatelessWidget { left: 20, child: _buildAvatar(context), ), - if (ModuleAuthorModel.showDynDecorate && - card.pendant?.image?.isNotEmpty == true) - Positioned( - top: 82.5, - left: -7.5, - child: IgnorePointer( - child: CachedNetworkImage( - width: 140, - height: 140, - imageUrl: card.pendant!.image!, - ), - ), - ), - if (card.officialVerify?.icon?.isNotEmpty == true || - (card.vip?.vipStatus ?? -1) > 0) - Positioned( - top: 172, - left: 82, - child: _buildBadge(context), - ), - if (live is Map && ((live['liveStatus'] as int?) ?? 0) == 1) - Positioned( - top: 180, - left: 20, - child: _buildLiveBadge(context), - ), Positioned( left: 120, top: 140, @@ -597,39 +541,6 @@ class UserInfoCard extends StatelessWidget { ); }); - _buildLiveBadge(context) => GestureDetector( - onTap: () { - Get.toNamed('/liveRoom?roomid=${live['roomid']}'); - }, - child: Container( - width: 85, - alignment: Alignment.center, - child: Badge( - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.equalizer_rounded, - size: MediaQuery.textScalerOf(context).scale(16), - color: Theme.of(context).colorScheme.onSecondaryContainer, - ), - Text( - '直播中', - style: TextStyle(height: 1), - ) - ], - ), - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 1, - ), - alignment: Alignment.center, - textColor: Theme.of(context).colorScheme.onSecondaryContainer, - backgroundColor: Theme.of(context).colorScheme.secondaryContainer, - ), - ), - ); - _buildH(BuildContext context) => Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, @@ -647,40 +558,7 @@ class UserInfoCard extends StatelessWidget { top: 10, bottom: card.prInfo?.content?.isNotEmpty == true ? 0 : 10, ), - child: Stack( - clipBehavior: Clip.none, - children: [ - _buildAvatar(context), - if (ModuleAuthorModel.showDynDecorate && - card.pendant?.image?.isNotEmpty == true) - Positioned( - top: -27.5, - left: -27.5, - child: IgnorePointer( - child: CachedNetworkImage( - width: 140, - height: 140, - imageUrl: card.pendant!.image!, - ), - ), - ), - if (card.officialVerify?.icon?.isNotEmpty == true || - (card.vip?.vipStatus ?? -1) > 0) - Positioned( - right: 0, - bottom: 0, - child: _buildBadge(context), - ), - if (live is Map && - ((live['liveStatus'] as int?) ?? 0) == 1) - Positioned( - left: 0, - bottom: -5, - right: 0, - child: _buildLiveBadge(context), - ), - ], - ), + child: _buildAvatar(context), ), Expanded( child: Column( diff --git a/lib/pages/search_panel/user/widgets/item.dart b/lib/pages/search_panel/user/widgets/item.dart index d60ffc4b..37d6897b 100644 --- a/lib/pages/search_panel/user/widgets/item.dart +++ b/lib/pages/search_panel/user/widgets/item.dart @@ -1,4 +1,4 @@ -import 'package:PiliPlus/common/widgets/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/avatar.dart'; import 'package:PiliPlus/models/search/result.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -25,35 +25,12 @@ class SearchUserItem extends StatelessWidget { child: Row( children: [ const SizedBox(width: 15), - Stack( - clipBehavior: Clip.none, - children: [ - NetworkImgLayer( - width: 42, - height: 42, - src: item.upic, - type: 'avatar', - ), - if (item.officialVerify?['type'] == 0 || - item.officialVerify?['type'] == 1) - Positioned( - bottom: 0, - right: 0, - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surface, - ), - child: Icon( - Icons.offline_bolt, - color: item.officialVerify?['type'] == 0 - ? Colors.yellow - : Colors.lightBlueAccent, - size: 14, - ), - ), - ), - ], + Avatar( + avatar: item.upic ?? '', + size: 42, + isVip: false, + officialType: item.officialVerify?['type'], + roomId: item.isLive == 1 ? item.roomId : null, ), const SizedBox(width: 10), Column( diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 3a99b4e4..b187c4fc 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:PiliPlus/common/widgets/avatar.dart'; import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; @@ -311,57 +312,25 @@ class _VideoInfoState extends State { mainAxisSize: MainAxisSize.min, children: [ Obx( - () => Stack( - clipBehavior: Clip.none, - children: [ - NetworkImgLayer( - type: 'avatar', - src: videoIntroController + () => Avatar( + avatar: videoIntroController + .userStat + .value['card']?['face'] ?? + '', + size: 35, + isVip: (videoIntroController .userStat .value['card'] - ?['face'] ?? - '', - width: 35, - height: 35, - fadeInDuration: Duration.zero, - fadeOutDuration: - Duration.zero, - ), - if ((videoIntroController - .userStat - .value['card'] - ?[ - 'official_verify'] - ?['type'] ?? - -1) != - -1) - Positioned( - right: -2, - bottom: -2, - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context) - .colorScheme - .surface, - ), - child: Icon( - Icons.offline_bolt, - color: videoIntroController - .userStat - .value['card'] - ?[ - 'official_verify'] - ?['type'] == - 0 - ? Colors.yellow - : Colors - .lightBlueAccent, - size: 14, - ), - ), - ), - ], + ?['vip']?['status'] ?? + -1) > + 0, + officialType: videoIntroController + .userStat.value['card'] + ?['official_verify']?['type'], + garbPendantImage: + videoIntroController.userStat + .value['card'] + ?['pendant']?['image'], ), ), const SizedBox(width: 10), diff --git a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart index ed471443..bccbf63c 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/widgets/avatar.dart'; import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/image_view.dart'; import 'package:PiliPlus/common/widgets/report.dart'; @@ -175,96 +176,18 @@ class ReplyItemGrpc extends StatelessWidget { ); } - Widget lfAvtar(BuildContext context) { - return Stack( - clipBehavior: Clip.none, - children: [ - if (ModuleAuthorModel.showDynDecorate && - replyItem.member.hasGarbPendantImage()) ...[ - Padding( - padding: const EdgeInsets.all(2), - child: NetworkImgLayer( - src: replyItem.member.face, - width: 30, - height: 30, - type: 'avatar', - ), - ), - Positioned( - left: -9, - top: -9, - child: IgnorePointer( - child: CachedNetworkImage( - width: 52, - height: 52, - imageUrl: replyItem.member.garbPendantImage, - ), - ), - ), - ] else - NetworkImgLayer( - src: replyItem.member.face, - width: 34, - height: 34, - type: 'avatar', - ), - if (replyItem.member.vipStatus > 0) - Positioned( - right: 0, - bottom: 0, - child: Container( - decoration: BoxDecoration( - //borderRadius: BorderRadius.circular(7), - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surface, - ), - child: Image.asset( - 'assets/images/big-vip.png', - height: 14, - semanticLabel: "大会员", - ), - ), - ), - //https://www.bilibili.com/blackboard/activity-whPrHsYJ2.html - if (replyItem.member.officialVerifyType == 0) - Positioned( - left: 0, - bottom: 0, - child: Container( - decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(8), - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surface, - ), - child: const Icon( - Icons.offline_bolt, - color: Colors.yellow, - size: 14, - semanticLabel: "认证个人", - ), - ), - ) - else if (replyItem.member.officialVerifyType == 1) - Positioned( - left: 0, - bottom: 0, - child: Container( - decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(8), - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surface, - ), - child: const Icon( - Icons.offline_bolt, - color: Colors.lightBlueAccent, - size: 14, - semanticLabel: "认证机构", - ), - ), - ), - ], - ); - } + Widget lfAvtar() => Avatar( + avatar: replyItem.member.face, + size: 34, + isVip: replyItem.member.vipStatus > 0, + officialType: replyItem.member.officialVerifyType.toInt(), + garbPendantImage: replyItem.member.hasGarbPendantImage() + ? replyItem.member.garbPendantImage + : null, + onTap: () { + feedBack(); + Get.toNamed('/member?mid=${replyItem.mid}'); + }); Widget content(BuildContext context) { return Column( @@ -272,83 +195,76 @@ class ReplyItemGrpc extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ /// fix Stack内GestureDetector onTap无效 - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - feedBack(); - Get.toNamed('/member?mid=${replyItem.mid}'); - }, - child: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - lfAvtar(context), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + lfAvtar(), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + replyItem.member.name, + style: TextStyle( + color: (replyItem.member.vipStatus > 0 && + replyItem.member.vipType == 2) + ? context.vipColor + : Theme.of(context).colorScheme.outline, + fontSize: 13, + ), + ), + const SizedBox(width: 6), + Image.asset( + 'assets/images/lv/lv${replyItem.member.isSeniorMember == 1 ? '6_s' : replyItem.member.level}.png', + height: 11, + semanticLabel: "等级:${replyItem.member.level}", + ), + const SizedBox(width: 6), + if (replyItem.mid == upMid) + const PBadge( + text: 'UP', + size: 'small', + stack: 'normal', + fs: 9, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + replyLevel == '' + ? DateTime.fromMillisecondsSinceEpoch( + replyItem.ctime.toInt() * 1000) + .toString() + .substring(0, 19) + : Utils.dateFormat(replyItem.ctime.toInt()), + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + if (replyItem.replyControl.location.isNotEmpty) Text( - replyItem.member.name, + ' • ${replyItem.replyControl.location}', style: TextStyle( - color: (replyItem.member.vipStatus > 0 && - replyItem.member.vipType == 2) - ? context.vipColor - : Theme.of(context).colorScheme.outline, - fontSize: 13, - ), + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: Theme.of(context).colorScheme.outline), ), - const SizedBox(width: 6), - Image.asset( - 'assets/images/lv/lv${replyItem.member.isSeniorMember == 1 ? '6_s' : replyItem.member.level}.png', - height: 11, - semanticLabel: "等级:${replyItem.member.level}", - ), - const SizedBox(width: 6), - if (replyItem.mid == upMid) - const PBadge( - text: 'UP', - size: 'small', - stack: 'normal', - fs: 9, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - replyLevel == '' - ? DateTime.fromMillisecondsSinceEpoch( - replyItem.ctime.toInt() * 1000) - .toString() - .substring(0, 19) - : Utils.dateFormat(replyItem.ctime.toInt()), - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - if (replyItem.replyControl.location.isNotEmpty) - Text( - ' • ${replyItem.replyControl.location}', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize, - color: Theme.of(context).colorScheme.outline), - ), - ], - ) - ], - ), - ], - ), + ], + ) + ], + ), + ], ), // title Padding(