yumi-flutter/lib/ui_kit/components/sc_compontent.dart
2026-06-15 16:21:05 +08:00

804 lines
23 KiB
Dart

import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
import 'package:tancent_vap/utils/constant.dart';
import 'package:tancent_vap/widgets/vap_view.dart';
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/app/constants/sc_screen.dart';
import 'package:yumi/shared/tools/sc_path_utils.dart';
import 'package:yumi/shared/tools/sc_room_profile_cache.dart';
import 'package:yumi/shared/tools/sc_network_image_utils.dart';
import 'package:yumi/ui_kit/components/sc_rotating_dots_loading.dart';
import '../../shared/data_sources/models/enum/sc_room_roles_type.dart';
const String kRoomCoverDefaultImg = "sc_images/general/sc_no_data.png";
int? _resolveImageCacheDimension(double? logicalSize) {
if (logicalSize == null || !logicalSize.isFinite || logicalSize <= 0) {
return null;
}
final devicePixelRatio =
WidgetsBinding
.instance
.platformDispatcher
.implicitView
?.devicePixelRatio ??
2.0;
final maxDimension = SCGlobalConfig.isLowPerformanceDevice ? 1280 : 1920;
final pixelSize = (logicalSize * devicePixelRatio).round();
return pixelSize.clamp(1, maxDimension).toInt();
}
String resolveRoomCoverUrl(String? roomId, String? roomCover) {
final cache = SCRoomProfileCache.getRoomProfile(roomId ?? "");
return SCRoomProfileCache.preferCachedValue(cache["roomCover"], roomCover) ??
"";
}
Widget head({
required String url,
required double width,
double? height,
Border? border,
String? headdress,
String? headdressCover,
BoxShape shape = BoxShape.circle,
BoxFit fit = BoxFit.cover,
BoxFit gifFit = BoxFit.contain,
BorderRadius? borderRadius,
bool showDefault = true,
bool isRoom = false,
}) {
final String headdressUrl = headdress?.trim() ?? "";
final String headdressCoverUrl = headdressCover?.trim() ?? "";
final String headdressExtension =
SCPathUtils.getFileExtension(headdressUrl).toLowerCase();
final bool hasLocalVirtualHeaddress = _isLocalAssetsVirtualUrl(headdressUrl);
final bool hasPictureHeaddress =
headdressUrl.isNotEmpty && SCPathUtils.fileTypeIsPic2(headdressUrl);
final bool hasAnimatedHeaddress =
headdressExtension == ".svga" || headdressExtension == ".mp4";
final bool shouldShowAnimatedHeaddress =
hasAnimatedHeaddress &&
SCGlobalConfig.allowsHighCostAnimations &&
!hasLocalVirtualHeaddress;
final bool shouldShowStaticHeaddressFallback =
hasAnimatedHeaddress &&
(!SCGlobalConfig.allowsHighCostAnimations || hasLocalVirtualHeaddress) &&
headdressCoverUrl.isNotEmpty;
final bool hasHeaddress =
headdressUrl.isNotEmpty || shouldShowStaticHeaddressFallback;
final double avatarScale =
hasPictureHeaddress ? 0.64 : (hasHeaddress ? 0.72 : 1);
final double avatarWidth = width * avatarScale;
final double avatarHeight = (height ?? width) * avatarScale;
Widget avatar = SizedBox(
width: avatarWidth,
height: avatarHeight,
child: netImage(
url: url,
width: avatarWidth,
height: avatarHeight,
fit: fit,
gifFit: gifFit,
noDefaultImg: !showDefault,
defaultImg: "sc_images/general/sc_icon_avar_defalt.png",
),
);
if (shape == BoxShape.circle) {
avatar = ClipOval(child: avatar);
if (border != null) {
avatar = Container(
width: avatarWidth,
height: avatarHeight,
decoration: ShapeDecoration(shape: CircleBorder(side: border.top)),
clipBehavior: Clip.antiAlias,
child: avatar,
);
}
} else if (borderRadius != null) {
avatar = ClipRRect(borderRadius: borderRadius, child: avatar);
if (border != null) {
avatar = Container(
width: avatarWidth,
height: avatarHeight,
decoration: BoxDecoration(border: border, borderRadius: borderRadius),
clipBehavior: Clip.antiAlias,
child: avatar,
);
}
} else if (border != null) {
avatar = Container(
width: avatarWidth,
height: avatarHeight,
decoration: BoxDecoration(border: border),
clipBehavior: Clip.antiAlias,
child: avatar,
);
}
return RepaintBoundary(
child: Stack(
alignment: Alignment.center,
children: [
avatar,
hasPictureHeaddress
? ColorFiltered(
colorFilter: const ColorFilter.matrix([
1,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
1,
0,
0,
-1,
-1,
-1,
3,
0,
]),
child: netImage(
url: headdressUrl,
width: width,
height: height ?? width,
fit: BoxFit.contain,
noDefaultImg: true,
),
)
: Container(),
shouldShowStaticHeaddressFallback
? IgnorePointer(
child: netImage(
url: headdressCoverUrl,
width: width,
height: height ?? width,
fit: BoxFit.contain,
noDefaultImg: true,
),
)
: Container(),
headdressUrl.isNotEmpty &&
headdressExtension == ".svga" &&
shouldShowAnimatedHeaddress
? IgnorePointer(
child: SVGAHeadwearWidget(
width: width,
height: width,
resource: headdressUrl,
),
)
: Container(),
headdressUrl.isNotEmpty &&
headdressExtension == ".mp4" &&
shouldShowAnimatedHeaddress
? SizedBox(
width: width,
height: width,
child: IgnorePointer(
child: VapView(
scaleType: ScaleType.centerCrop,
repeat: 90000000,
onViewCreated: (controller) {
FileCacheManager.getInstance()
.getFile(url: headdressUrl)
.then((file) {
controller.playFile(file.path);
});
},
),
),
)
: Container(),
],
),
);
}
bool _isLocalAssetsVirtualUrl(String value) {
final uri = Uri.tryParse(value.trim());
return uri?.host.toLowerCase() == "local.assets";
}
Widget netImage({
required String url,
String? defaultImg,
bool noDefaultImg = false,
double? width,
double? height,
BorderRadius? borderRadius,
BoxFit? fit,
BoxFit? gifFit,
AlignmentGeometry? gifAlignment,
BoxShape? shape,
Border? border,
Widget? loadingWidget,
Widget? errorWidget,
}) {
final isGif = SCPathUtils.getFileExtension(url) == ".gif";
final resolvedHeight = height ?? width;
final resolvedFit =
isGif ? (gifFit ?? fit ?? BoxFit.cover) : (fit ?? BoxFit.cover);
final resolvedAlignment =
isGif ? (gifAlignment ?? Alignment.center) : Alignment.center;
return ExtendedImage.network(
url,
width: width,
height: resolvedHeight,
cacheWidth: isGif ? null : _resolveImageCacheDimension(width),
cacheHeight: isGif ? null : _resolveImageCacheDimension(resolvedHeight),
headers: buildNetworkImageHeaders(url),
fit: resolvedFit,
alignment: resolvedAlignment,
cache: true,
shape: shape ?? BoxShape.rectangle,
borderRadius: borderRadius,
clearMemoryCacheWhenDispose: false,
clearMemoryCacheIfFailed: true,
border: border,
filterQuality: FilterQuality.low,
gaplessPlayback: true,
loadStateChanged: (ExtendedImageState state) {
if (state.extendedImageLoadState == LoadState.completed) {
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
width: width,
height: resolvedHeight,
fit: resolvedFit,
alignment: resolvedAlignment,
);
} else if (state.extendedImageLoadState == LoadState.failed) {
if (errorWidget != null) {
return errorWidget;
}
return noDefaultImg
? Container()
: Image.asset(
defaultImg ?? "sc_images/general/sc_icon_loading.png",
fit: BoxFit.cover,
);
} else {
if (loadingWidget != null) {
return loadingWidget;
}
return noDefaultImg ? Container() : const SCRotatingDotsLoading();
}
},
);
}
/// 空页面
///默认空页面
mainEmpty({
String msg = "No data",
Widget? image,
bool center = true,
Color textColor = const Color(0xffffffff),
}) {
List<Widget> list = [];
list.add(SizedBox(height: height(center ? 40.w : 0.w)));
list.add(
image ??
Image.asset(
'sc_images/general/sc_icon_loading.png',
width: 121.w,
fit: BoxFit.fitWidth,
),
);
if (msg.isNotEmpty) {
list.add(SizedBox(height: height(10.w)));
list.add(
Text(
msg,
style: TextStyle(
fontSize: sp(12),
color: textColor,
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
height: 1,
),
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment:
center ? MainAxisAlignment.center : MainAxisAlignment.start,
children: list,
),
],
);
}
msgRoleTag(String role, {double? width, double? height}) {
String image = "";
if (SCRoomRolesType.HOMEOWNER.name == role) {
image = 'fz';
} else if (SCRoomRolesType.ADMIN.name == role) {
image = "gly";
} else if (SCRoomRolesType.MEMBER.name == role) {
image = "hy";
} else if (SCRoomRolesType.TOURIST.name == role) {
image = "guest";
}
if (image.isEmpty) return SizedBox.shrink();
return Container(
alignment: Alignment.center,
width: width ?? 22.w,
height: height ?? 22.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("sc_images/room/sc_icon_room_$image.png"),
),
),
);
}
///性别 0女 1男
xb(num? sex, {double? height, Color? color = Colors.white}) {
// if (sex == null) return Container();
String image = "sc_images/login/sc_icon_sex_man.png";
if (sex == 0) {
image = "sc_images/login/sc_icon_sex_woman.png";
}
return Image.asset(
image,
height: height ?? 14.w,
fit: BoxFit.fill,
color: color,
);
}
xb2(num? sex, {double? width, double? height}) {
if (sex == null) return Container();
String image = "sc_images/login/sc_icon_sex_man_bg.png";
if (sex == 0) {
image = "sc_images/login/sc_icon_sex_woman_bg.png";
}
return Image.asset(
image,
width: width ?? 28.w,
height: height ?? 14.w,
fit: BoxFit.fill,
);
}
///搜索框
searchWidget({
Function? onChange,
Function? onTap,
String? hint,
double? hintSize,
Color? borderColor,
EdgeInsetsGeometry? padding,
TextEditingController? controller,
Color? textColor,
Color? serachIconColor,
FocusNode? focusNode,
}) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (onTap != null) {
onTap();
}
},
child: Container(
margin:
padding ??
EdgeInsetsDirectional.only(start: width(15), end: width(0)),
alignment: AlignmentDirectional.centerStart,
height: width(36),
//width: width(345),
decoration: BoxDecoration(
color: const Color(0xff18F2B1).withValues(alpha: 0.1),
borderRadius: BorderRadius.all(Radius.circular(height(20))),
border: Border.all(color: borderColor ?? Colors.black12, width: 1.w),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(width: width(10)),
Image.asset(
"sc_images/index/sc_icon_serach2.png",
width: 20.w,
color: serachIconColor ?? Color(0xff18F2B1),
height: 20.w,
),
SizedBox(width: width(6)),
_buildPhoneInput(
onChange,
onTap == null,
hint ?? 'Search by ID',
controller,
textColor,
hintSize,
focusNode,
),
],
),
),
);
}
_buildPhoneInput(
Function? onChange,
bool enabled,
String tint,
TextEditingController? controller,
Color? textColor,
double? hintSize,
FocusNode? focusNode,
) {
return Expanded(
child: TextField(
controller: controller,
onChanged: (text) {
if (onChange != null) {
onChange(text);
}
},
focusNode: focusNode,
autofocus: false,
enabled: enabled,
maxLength: 11,
cursorColor: SocialChatTheme.primaryColor,
decoration: InputDecoration(
hintText: tint,
hintStyle: TextStyle(
color: SocialChatTheme.textSecondary,
fontSize: hintSize ?? sp(14),
),
counterText: '',
isDense: true,
filled: false,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
contentPadding: EdgeInsets.only(bottom: 0),
),
style: TextStyle(
textBaseline: TextBaseline.alphabetic,
fontSize: hintSize ?? sp(14),
color: textColor ?? Colors.black,
),
),
);
}
///vip标识
getVIPBadge(String? type, {double? width, double? height}) {
final level = _vipBadgeLevel(type);
if (level <= 0) {
return const SizedBox.shrink();
}
final assetLevel = level.clamp(1, 5).toInt();
return Image.asset(
"sc_images/vip/sc_vip_badge_$assetLevel.png",
width: width,
height: height,
fit: BoxFit.contain,
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
);
}
int _vipBadgeLevel(String? type) {
final text = type?.trim() ?? "";
if (text.isEmpty) {
return 0;
}
final plain = int.tryParse(text);
if ((plain ?? 0) > 0) {
return plain!;
}
final vipMatch = RegExp(
r'(?:S?VIP|NOBLE[_\s-]*VIP)[^\d]*([1-9]\d*)',
caseSensitive: false,
).firstMatch(text);
if (vipMatch != null) {
return int.tryParse(vipMatch.group(1) ?? "") ?? 0;
}
final trailingMatch = RegExp(r'([1-9]\d*)$').firstMatch(text);
if (trailingMatch != null) {
return int.tryParse(trailingMatch.group(1) ?? "") ?? 0;
}
return RegExp(r'S?VIP|NOBLE[_\s-]*VIP', caseSensitive: false).hasMatch(text)
? 1
: 0;
}
///用户等级
getUserLevel(
num level, {
double? width,
double? height,
double? fontSize,
double textOffsetX = 0,
}) {
String icon = "";
if (level > 0 && level < 11) {
icon = "sc_images/level/sc_icon_user_level_1_10.png";
} else if (level > 10 && level < 21) {
icon = "sc_images/level/sc_icon_user_level_10_20.png";
} else if (level > 20 && level < 31) {
icon = "sc_images/level/sc_icon_user_level_20_30.png";
} else if (level > 30 && level < 41) {
icon = "sc_images/level/sc_icon_user_level_30_40.png";
} else if (level > 40 && level < 51) {
icon = "sc_images/level/sc_icon_user_level_40_50.png";
}
return icon.isNotEmpty
? Directionality(
textDirection: TextDirection.ltr,
child: Stack(
alignment: AlignmentDirectional.center,
children: [
Image.asset(icon, width: width, height: height, fit: BoxFit.fill),
PositionedDirectional(
end: level > 9 ? 10.w : 15.w,
child: Transform.translate(
offset: Offset(textOffsetX.w, 0),
child: text(
"$level",
fontSize: fontSize ?? 12.sp,
fontWeight: FontWeight.w600,
),
),
),
],
),
)
: Container();
}
///用户魅力等级
getWealthLevel(
num level, {
double? width,
double? height,
double? fontSize,
double textOffsetX = 0,
}) {
String icon = "";
if (level > 0 && level < 11) {
icon = "sc_images/level/sc_icon_wealth_level_1_10.png";
} else if (level > 10 && level < 21) {
icon = "sc_images/level/sc_icon_wealth_level_10_20.png";
} else if (level > 20 && level < 31) {
icon = "sc_images/level/sc_icon_wealth_level_20_30.png";
} else if (level > 30 && level < 41) {
icon = "sc_images/level/sc_icon_wealth_level_30_40.png";
} else if (level > 40 && level < 51) {
icon = "sc_images/level/sc_icon_wealth_level_40_50.png";
}
return icon.isNotEmpty
? Directionality(
textDirection: TextDirection.ltr,
child: Stack(
alignment: AlignmentDirectional.center,
children: [
Image.asset(icon, width: width, height: height, fit: BoxFit.fill),
PositionedDirectional(
end: level > 9 ? 10.w : 15.w,
child: Transform.translate(
offset: Offset(textOffsetX.w, 0),
child: text(
"$level",
fontSize: fontSize ?? 12.sp,
fontWeight: FontWeight.w600,
),
),
),
],
),
)
: Container();
}
String getGiftObtCoinsBg(num obtCoins) {
String icon = "";
if (obtCoins > -1 && obtCoins < 5000) {
icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_0.png";
} else if (obtCoins > 4999 && obtCoins < 50000) {
icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_1.png";
} else if (obtCoins > 49999 && obtCoins < 100000) {
icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_2.png";
} else if (obtCoins > 99999 && obtCoins < 1000000) {
icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_3.png";
} else if (obtCoins > 999999 && obtCoins < 10000000) {
icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_4.png";
} else if (obtCoins > 9999999) {
icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_5.png";
}
return icon;
}
///数字图片
buildNum(String num, {double? size = 18}) {
if (num.isNotEmpty) {
List<Widget> eList = [];
int index = 0;
num.split("").forEach((item) {
if (item == "k") {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.9).w,
),
child: Image.asset(
"sc_images/room/sc_icon_k.png",
width: size,
height: size,
),
),
);
} else if (item == "m") {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.9).w,
),
child: Image.asset(
"sc_images/room/sc_icon_m.png",
width: size,
height: size,
),
),
);
} else {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.8).w,
),
child: Image.asset(
"sc_images/room/sc_icon_number_$item.png",
width: size,
height: size,
),
),
);
}
index = index + 1;
});
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(children: eList),
);
} else {
return Container();
}
}
///游戏飘屏数字图片
buildNumForGame(String num, {double? size = 18}) {
if (num.isNotEmpty) {
List<Widget> eList = [];
int index = 0;
num.split("").forEach((item) {
if (item == "k") {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.65).w,
),
child: Image.asset(
"sc_images/general/sc_icon_game_numk.png",
width: size,
height: size,
),
),
);
} else if (item == "m") {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.65).w,
),
child: Image.asset(
"sc_images/general/sc_icon_game_numm.png",
width: size,
height: size,
),
),
);
} else {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.6).w,
),
child: Image.asset(
"sc_images/general/sc_icon_game_num$item.png",
width: size,
height: size,
),
),
);
}
index = index + 1;
});
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(children: eList),
);
} else {
return Container();
}
}
///房间幸运数字
buildNumForRoomLuckNum(String num, {double? size = 18}) {
if (num.isNotEmpty) {
List<Widget> eList = [];
int index = 0;
num.split("").forEach((item) {
eList.add(
Container(
margin: EdgeInsetsDirectional.only(
start: (index * (size ?? 18) * 0.7).w,
),
child: Image.asset(
"sc_images/room/sc_icon_lucknumber_$item.png",
width: size,
height: size,
),
),
);
index = index + 1;
});
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(children: eList),
);
} else {
return Container();
}
}
buildFamilyProgressLinearGradient(int level) {
if (level == 0) {
return LinearGradient(colors: [Color(0xff2E4957), Color(0xffC9E7E8)]);
} else if (level == 1) {
return LinearGradient(colors: [Color(0xff5A3F31), Color(0xffF8CD85)]);
} else if (level == 2) {
return LinearGradient(colors: [Color(0xff0D5ACE), Color(0xffCBD9E8)]);
} else if (level == 3) {
return LinearGradient(colors: [Color(0xffC57E1C), Color(0xffCBD9E8)]);
} else if (level == 10) {
return LinearGradient(colors: [Color(0xff3C7FBE), Color(0xffADE1F1)]);
} else if (level == 11) {
return LinearGradient(colors: [Color(0xff300A67), Color(0xffA53EFF)]);
}
return LinearGradient(colors: [Color(0xff2E4957), Color(0xffC9E7E8)]);
}
buildVipNameColor(String type) {
return null;
}