Files
ReelShort/lib/pages/reward/reward_visuals.dart
T

197 lines
6.0 KiB
Dart

// Author: fruit.com.cn
import 'package:flutter/material.dart';
import '../../core/data/models/reward.dart';
import '../../core/theme/app_colors.dart';
const double _coinInnerInsetRatio = 0.1;
const double _coinLetterRatio = 0.46;
const double _coinHighlightRatio = 0.18;
const double _coinHighlightOpacity = 0.54;
const double _coinLetterShadowBlur = 1.5;
const double _coinLetterShadowOffset = 0.8;
const double _taskIconSize = 46;
const double _taskIconRadius = 12;
const double _taskGlyphSize = 24;
const double _adIconWidth = 28;
const double _adIconHeight = 22;
const double _adIconRadius = 4;
const double _adUnderlineWidth = 12;
const double _adUnderlineHeight = 2;
const double _adUnderlineBottom = -5;
const Color _coinOuter = Color(0xFFF6B84E);
const Color _coinInnerStart = Color(0xFFFFD98B);
const Color _coinInnerEnd = Color(0xFFE39B31);
const Color _coinBorder = Color(0xFFFFE0A1);
const Color _coinHighlight = Color(0xFFFFF4CC);
const Color _coinLetterShadow = Color(0xFFB66A16);
const Color _taskIconStart = Color(0xFF4C171E);
const Color _taskIconEnd = Color(0xFF281014);
const Color _taskIconSurface = Color(0xFF202022);
/// 奖励中心统一金币图标。
class RewardCoinIcon extends StatelessWidget {
const RewardCoinIcon({super.key, required this.size});
final double size;
@override
Widget build(BuildContext context) {
final inset = size * _coinInnerInsetRatio;
return Container(
width: size,
height: size,
padding: EdgeInsets.all(inset),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: _coinOuter,
boxShadow: [BoxShadow(color: Color(0x4DF6B84E), blurRadius: 6)],
),
child: Stack(
alignment: Alignment.center,
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [_coinInnerStart, _coinInnerEnd],
),
border: Border.all(color: _coinBorder),
),
),
Positioned(
top: size * 0.12,
left: size * 0.18,
child: Container(
width: size * _coinHighlightRatio,
height: size * _coinHighlightRatio,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _coinHighlight.withValues(alpha: _coinHighlightOpacity),
),
),
),
Text(
'N',
style: TextStyle(
color: AppColors.textPrimary,
fontSize: size * _coinLetterRatio,
fontWeight: FontWeight.w900,
height: 1,
shadows: const [
Shadow(
color: _coinLetterShadow,
blurRadius: _coinLetterShadowBlur,
offset: Offset(
_coinLetterShadowOffset,
_coinLetterShadowOffset,
),
),
],
),
),
],
),
);
}
}
/// 奖励中心签到按钮的广告标识。
class RewardAdIcon extends StatelessWidget {
const RewardAdIcon({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: _adIconWidth,
height: _adIconHeight,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Container(
width: _adIconWidth,
height: _adIconHeight,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: AppColors.textPrimary, width: 2),
borderRadius: BorderRadius.circular(_adIconRadius),
),
child: const Text(
'AD',
style: TextStyle(
color: AppColors.textPrimary,
fontSize: 11,
fontWeight: FontWeight.w900,
height: 1,
),
),
),
Positioned(
bottom: _adUnderlineBottom,
child: Container(
width: _adUnderlineWidth,
height: _adUnderlineHeight,
decoration: BoxDecoration(
color: AppColors.textPrimary,
borderRadius: BorderRadius.circular(_adUnderlineHeight),
),
),
),
],
),
);
}
}
/// 根据后台任务类型绘制任务图标,不绑定具体任务文案。
class RewardTaskLeading extends StatelessWidget {
const RewardTaskLeading({super.key, required this.task});
final RewardTask task;
@override
Widget build(BuildContext context) {
final invite = _isInviteTask(task);
return Container(
width: _taskIconSize,
height: _taskIconSize,
decoration: BoxDecoration(
shape: invite ? BoxShape.circle : BoxShape.rectangle,
borderRadius: invite ? null : BorderRadius.circular(_taskIconRadius),
gradient: invite
? const LinearGradient(colors: [_taskIconStart, _taskIconEnd])
: null,
color: invite ? null : _taskIconSurface,
),
child: Icon(
_taskIcon(task),
color: AppColors.textPrimary,
size: _taskGlyphSize,
),
);
}
}
bool _isInviteTask(RewardTask task) {
final code = task.code.toUpperCase();
return task.type == 'INVITE_FRIEND' || code.contains('INVITE');
}
IconData _taskIcon(RewardTask task) {
final code = task.code.toUpperCase();
if (code.contains('FACEBOOK')) return Icons.facebook_rounded;
if (code.contains('YOUTUBE')) return Icons.smart_display_rounded;
return switch (task.type) {
'WATCH_DURATION' => Icons.play_circle_outline_rounded,
'WATCH_EPISODES' => Icons.video_collection_outlined,
'COMPLETE_PROFILE' => Icons.person_outline_rounded,
'FIRST_PURCHASE' => Icons.shopping_bag_outlined,
'INVITE_FRIEND' => Icons.card_giftcard_rounded,
_ => Icons.stars_rounded,
};
}