yumi-flutter/lib/modules/user/vip/vip_benefit_page.dart

393 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
class VipBenefitPage extends StatefulWidget {
const VipBenefitPage({super.key, this.initialIndex = 0});
final int initialIndex;
@override
State<VipBenefitPage> createState() => _VipBenefitPageState();
}
class _VipBenefitPageState extends State<VipBenefitPage> {
static const List<_VipBenefitData> _benefits = [
_VipBenefitData(
title: 'Gif Profile Picture',
description: 'You can upload a Gif image as your profile picture',
icon: Icons.image_outlined,
),
_VipBenefitData(
title: 'Colorful Nickname',
description: 'Your nickname can use a special colorful display effect',
icon: Icons.notes_rounded,
),
_VipBenefitData(
title: 'Gif Room Picture',
description: 'You can use a Gif image as your room cover picture',
icon: Icons.home_rounded,
),
_VipBenefitData(
title: 'Colorful ID',
description: 'Your user ID can use a highlighted colorful style',
icon: Icons.pin_rounded,
isIdIcon: true,
),
];
late int _selectedIndex;
@override
void initState() {
super.initState();
_selectedIndex = widget.initialIndex.clamp(0, _benefits.length - 1).toInt();
}
@override
Widget build(BuildContext context) {
final selected = _benefits[_selectedIndex];
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: const [Color(0xFF0D2B22), Color(0xFF0E0E0E)],
stops: const [0, 0.40],
),
),
),
),
Positioned(
left: 0,
right: 0,
top: 0,
child: Image.asset(
'sc_images/vip/sc_vip_page_bg.png',
width: ScreenUtil().screenWidth,
fit: BoxFit.fitWidth,
alignment: Alignment.topCenter,
),
),
Positioned(
left: 0,
right: 0,
top: 0,
height: 300.w,
child: IgnorePointer(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withValues(alpha: 0.36),
Colors.black.withValues(alpha: 0.62),
Colors.transparent,
],
stops: const [0, 0.52, 1],
),
),
),
),
),
SafeArea(
bottom: false,
child: Column(
children: [
_buildTopBar(context),
SizedBox(height: 18.w),
_buildBenefitTabs(),
SizedBox(height: 36.w),
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.fromLTRB(36.w, 0, 36.w, 32.w),
child: _buildBenefitCard(selected),
),
),
],
),
),
],
),
);
}
Widget _buildTopBar(BuildContext context) {
return SizedBox(
height: 44.w,
child: Stack(
alignment: Alignment.center,
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: SizedBox(
width: 44.w,
height: 44.w,
child: Icon(
SCGlobalConfig.lang == 'ar'
? Icons.keyboard_arrow_right
: Icons.keyboard_arrow_left,
color: Colors.white,
size: 28.w,
),
),
),
),
Text(
'VIP Benefits',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
Widget _buildBenefitTabs() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int index = 0; index < _benefits.length; index++)
_buildBenefitTab(index, _benefits[index]),
],
);
}
Widget _buildBenefitTab(int index, _VipBenefitData item) {
final selected = index == _selectedIndex;
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _selectedIndex = index),
child: SizedBox(
width: 58.w,
child: Column(
children: [
Container(
width: 54.w,
height: 54.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
color:
selected
? Colors.white.withValues(alpha: 0.08)
: Colors.transparent,
),
alignment: Alignment.center,
child: _buildBenefitIcon(item, size: 28.w),
),
SizedBox(height: 8.w),
AnimatedOpacity(
duration: const Duration(milliseconds: 160),
opacity: selected ? 1 : 0,
child: Container(
width: 20.w,
height: 3.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(999.w),
),
),
),
],
),
),
);
}
Widget _buildBenefitCard(_VipBenefitData item) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: const Color(0xFF171717),
borderRadius: BorderRadius.circular(12.w),
),
padding: EdgeInsets.fromLTRB(14.w, 22.w, 14.w, 14.w),
child: Column(
children: [
Text(
item.title,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 14.w),
Text(
item.description,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.58),
fontSize: 14.sp,
fontWeight: FontWeight.w400,
height: 1.22,
),
),
SizedBox(height: 18.w),
_buildPreviewPlaceholder(item),
],
),
);
}
Widget _buildPreviewPlaceholder(_VipBenefitData item) {
return Container(
width: double.infinity,
height: 260.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.w),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: const [Color(0xFF234F73), Color(0xFFF5F5F5)],
stops: const [0, 0.58],
),
),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
left: 18.w,
top: 16.w,
child: Icon(
SCGlobalConfig.lang == 'ar'
? Icons.keyboard_arrow_right
: Icons.keyboard_arrow_left,
color: Colors.white,
size: 24.w,
),
),
Positioned(
top: 80.w,
child: Container(
width: 64.w,
height: 64.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
border: Border.all(color: Colors.white, width: 2.w),
),
alignment: Alignment.center,
child: _buildBenefitIcon(item, size: 34.w),
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 110.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(18.w),
topRight: Radius.circular(18.w),
bottomLeft: Radius.circular(12.w),
bottomRight: Radius.circular(12.w),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
item.title,
style: TextStyle(
color: const Color(0xFF333333),
fontSize: 17.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 10.w),
Container(
width: 88.w,
height: 18.w,
decoration: BoxDecoration(
color: const Color(0xFFF0F0F0),
borderRadius: BorderRadius.circular(9.w),
),
alignment: Alignment.center,
child: Text(
'Placeholder',
style: TextStyle(
color: const Color(0xFF555555),
fontSize: 9.sp,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
],
),
);
}
Widget _buildBenefitIcon(_VipBenefitData item, {required double size}) {
if (item.isIdIcon) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.w),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFF7244), Color(0xFFFFD84E)],
),
),
alignment: Alignment.center,
child: Text(
'ID',
style: TextStyle(
color: Colors.white,
fontSize: (size * 0.42).sp,
fontWeight: FontWeight.w800,
height: 1,
),
),
);
}
return ShaderMask(
shaderCallback:
(bounds) => const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFF6F3D), Color(0xFFFFD84E)],
).createShader(bounds),
blendMode: BlendMode.srcIn,
child: Icon(item.icon, color: Colors.white, size: size),
);
}
}
class _VipBenefitData {
const _VipBenefitData({
required this.title,
required this.description,
required this.icon,
this.isIdIcon = false,
});
final String title;
final String description;
final IconData icon;
final bool isIdIcon;
}