chatapp3-flutter/lib/ui_kit/widgets/id/sc_special_id_badge.dart
2026-04-22 20:08:45 +08:00

247 lines
6.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:yumi/ui_kit/widgets/svga/sc_svga_asset_widget.dart';
class SCSpecialIdAssets {
static const String roomId = 'sc_images/general/room_id.svga';
static const String roomOwnerId = 'sc_images/general/room_id_custom.svga';
static const String userId = 'sc_images/general/user_id.svga';
static const String userIdLarge = 'sc_images/general/user_id_4.svga';
}
const LinearGradient _scSpecialIdVipTextGradient = LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color(0xFFFFD24D),
Color(0xFFFF7A4D),
Color(0xFFFF4FB3),
Color(0xFF8E63FF),
Color(0xFF38D6FF),
],
);
class SCSpecialIdBadge extends StatelessWidget {
const SCSpecialIdBadge({
super.key,
required this.idText,
this.showAnimated = false,
this.assetPath,
this.animationWidth = 120,
this.animationHeight = 28,
this.textPadding = EdgeInsets.zero,
this.showTextWhenAnimated = false,
this.showTextBesideAnimated = false,
this.animatedTextSpacing = 0,
this.animatedTextPrefix = '',
this.animationTextStyle,
this.normalTextStyle,
this.normalPrefix = 'ID:',
this.showCopyIcon = false,
this.showCopyIconWhenAnimated = false,
this.copyIconAssetPath = 'sc_images/room/sc_icon_user_card_copy_id.png',
this.copyIconSize = 12,
this.copyIconSpacing = 5,
this.copyIconColor,
this.loop = true,
this.active = true,
this.animationFit = BoxFit.fill,
this.inlineAnimationWidth,
this.showAnimatedGradientText = false,
this.animatedTextGradient,
});
final String idText;
final bool showAnimated;
final String? assetPath;
final double animationWidth;
final double animationHeight;
final EdgeInsetsGeometry textPadding;
final bool showTextWhenAnimated;
final bool showTextBesideAnimated;
final double animatedTextSpacing;
final String animatedTextPrefix;
final TextStyle? animationTextStyle;
final TextStyle? normalTextStyle;
final String normalPrefix;
final bool showCopyIcon;
final bool showCopyIconWhenAnimated;
final String copyIconAssetPath;
final double copyIconSize;
final double copyIconSpacing;
final Color? copyIconColor;
final bool loop;
final bool active;
final BoxFit animationFit;
final double? inlineAnimationWidth;
final bool showAnimatedGradientText;
final Gradient? animatedTextGradient;
@override
Widget build(BuildContext context) {
final normalizedId = idText.trim();
final shouldAnimate =
showAnimated &&
(assetPath?.isNotEmpty ?? false) &&
normalizedId.isNotEmpty;
final children = <Widget>[
shouldAnimate
? (showTextWhenAnimated
? _buildAnimatedBadge(normalizedId)
: (showTextBesideAnimated
? _buildAnimatedInlineBadge(normalizedId)
: _buildAnimatedAsset()))
: _buildPlainBadge(normalizedId),
];
if (showCopyIcon && (!shouldAnimate || showCopyIconWhenAnimated)) {
children.add(SizedBox(width: copyIconSpacing));
children.add(
Image.asset(
copyIconAssetPath,
width: copyIconSize,
height: copyIconSize,
color: copyIconColor,
),
);
}
return Row(
mainAxisSize: MainAxisSize.min,
textDirection: TextDirection.ltr,
children: children,
);
}
Widget _buildAnimatedAsset() {
return SizedBox(
width: animationWidth,
height: animationHeight,
child: SCSvgaAssetWidget(
assetPath: assetPath!,
width: animationWidth,
height: animationHeight,
active: active,
loop: loop,
fit: animationFit,
fallback: const SizedBox.shrink(),
),
);
}
Widget _buildAnimatedInlineBadge(String normalizedId) {
final compactAnimationWidth = inlineAnimationWidth ?? animationHeight;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: compactAnimationWidth,
height: animationHeight,
child: SCSvgaAssetWidget(
assetPath: assetPath!,
width: compactAnimationWidth,
height: animationHeight,
active: active,
loop: loop,
fit: animationFit,
fallback: const SizedBox.shrink(),
),
),
SizedBox(width: animatedTextSpacing),
_buildGradientAwareText('$animatedTextPrefix$normalizedId'),
],
);
}
Widget _buildGradientAwareText(
String text, {
TextStyle? styleOverride,
TextOverflow overflow = TextOverflow.ellipsis,
}) {
final style =
styleOverride ??
animationTextStyle ??
const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700,
);
if (!showAnimatedGradientText) {
return Text(text, maxLines: 1, overflow: overflow, style: style);
}
return ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) {
final shaderBounds = Rect.fromLTWH(
0,
0,
bounds.width <= 0 ? 1 : bounds.width,
bounds.height <= 0 ? 1 : bounds.height,
);
return (animatedTextGradient ?? _scSpecialIdVipTextGradient)
.createShader(shaderBounds);
},
child: Text(
text,
maxLines: 1,
overflow: overflow,
style: style.copyWith(color: Colors.white),
),
);
}
Widget _buildAnimatedBadge(String normalizedId) {
return SizedBox(
width: animationWidth,
height: animationHeight,
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned.fill(
child: SCSvgaAssetWidget(
assetPath: assetPath!,
width: animationWidth,
height: animationHeight,
active: active,
loop: loop,
fit: animationFit,
fallback: const SizedBox.shrink(),
),
),
Positioned.fill(
child: Padding(
padding: textPadding,
child: Align(
alignment: Alignment.centerLeft,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: _buildGradientAwareText(
normalizedId,
overflow: TextOverflow.visible,
),
),
),
),
),
],
),
);
}
Widget _buildPlainBadge(String normalizedId) {
return _buildGradientAwareText(
'$normalPrefix$normalizedId',
styleOverride:
normalTextStyle ??
const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
);
}
}