393 lines
11 KiB
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;
|
|
}
|