import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/modules/user/vip/vip_route.dart'; class VipDetailPage extends StatefulWidget { const VipDetailPage({super.key}); @override State createState() => _VipDetailPageState(); } class _VipDetailPageState extends State { int _selectedLevel = 1; late bool _showUpdateButton; @override void initState() { super.initState(); _showUpdateButton = false; } @override Widget build(BuildContext context) { 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(), _buildLevelTabs(), Expanded( child: SingleChildScrollView( physics: const BouncingScrollPhysics(), padding: EdgeInsets.fromLTRB(12.w, 40.w, 12.w, 18.w), child: Column( children: [ _buildLevelCard(), SizedBox(height: 30.w), _buildLevelDivider(), SizedBox(height: 3.w), _buildSectionTitle('Decoration Privileges'), SizedBox(height: 10.w), _buildDecorationGrid(), SizedBox(height: 20.w), _buildSectionTitle('VIP Privileges'), SizedBox(height: 14.w), _buildPrivilegeGrid(), SizedBox(height: 76.w), ], ), ), ), _buildActivateBar(), ], ), ), ], ), ); } Widget _buildTopBar() { 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', style: TextStyle( color: Colors.white, fontSize: 18.sp, fontWeight: FontWeight.w500, ), ), Align( alignment: AlignmentDirectional.centerEnd, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => SCNavigatorUtils.push(context, VipRoute.instruction), child: Padding( padding: EdgeInsetsDirectional.only(end: 12.w), child: Image.asset( 'sc_images/vip/sc_vip_question.png', width: 24.w, height: 24.w, ), ), ), ), ], ), ); } Widget _buildLevelTabs() { return SizedBox( height: 43.w, child: ListView.separated( scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), padding: EdgeInsets.symmetric(horizontal: 12.w), itemCount: 5, separatorBuilder: (_, __) => SizedBox(width: 30.w), itemBuilder: (context, index) { final level = index + 1; final selected = level == _selectedLevel; return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => setState(() => _selectedLevel = level), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: 4.w), Text( 'VIP$level', style: TextStyle( color: selected ? Colors.white : Colors.white.withValues(alpha: 0.42), fontSize: 15.sp, fontWeight: FontWeight.w500, ), ), SizedBox(height: 6.w), SizedBox( width: 0, height: 9.5.w, child: OverflowBox( minWidth: 60.w, maxWidth: 60.w, alignment: Alignment.center, child: selected ? Image.asset( 'sc_images/vip/sc_vip_selected_line.png', width: 60.w, height: 9.5.w, fit: BoxFit.fill, ) : const SizedBox.shrink(), ), ), ], ), ); }, ), ); } Widget _buildLevelCard() { final assetLevel = _assetLevel; return Center( child: SizedBox( width: 351.w, height: 124.5.w, child: Stack( clipBehavior: Clip.none, children: [ Positioned.fill( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.w), border: Border.all( color: const Color(0xFFEBCB76), width: 1.w, ), image: DecorationImage( image: AssetImage( 'sc_images/vip/sc_vip_card_bg_$assetLevel.png', ), fit: BoxFit.fill, ), ), ), ), PositionedDirectional( start: 20.w, top: 33.w, child: _buildVipLevelMark(_selectedLevel, scale: 1.18), ), PositionedDirectional( start: 22.w, top: 80.w, child: Text( 'Not activated', style: TextStyle( color: const Color(0xFFF2C766).withValues(alpha: 0.78), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ), PositionedDirectional( end: 24.w, top: -36.w, child: Image.asset( 'sc_images/vip/sc_vip_badge_$assetLevel.png', width: 135.w, height: 135.w, fit: BoxFit.contain, ), ), ], ), ), ); } Widget _buildLevelDivider() { final ornamentLayout = _ornamentLayoutForLevel(_assetLevel); return SizedBox( height: 38.w, child: Stack( clipBehavior: Clip.none, alignment: Alignment.center, children: [ Positioned( left: -12.w, right: -12.w, top: 0, height: 8.w, child: Image.asset( 'sc_images/vip/sc_vip_top_border_$_assetLevel.png', fit: BoxFit.fill, ), ), Positioned( left: 0, right: 0, top: ornamentLayout.top.w, child: Transform.translate( offset: Offset(ornamentLayout.dx.w, 0), child: Center( child: Image.asset( 'sc_images/vip/sc_vip_card_ornament_$_assetLevel.png', width: ornamentLayout.width.w, height: ornamentLayout.height.w, fit: BoxFit.contain, ), ), ), ), ], ), ); } Widget _buildSectionTitle(String title) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( 'sc_images/vip/sc_vip_title_left.png', width: 16.w, height: 20.w, fit: BoxFit.contain, ), SizedBox(width: 8.w), Text( title, style: TextStyle( color: Colors.white, fontSize: 18.sp, fontWeight: FontWeight.w500, ), ), SizedBox(width: 8.w), Image.asset( 'sc_images/vip/sc_vip_title_right.png', width: 16.w, height: 20.w, fit: BoxFit.contain, ), ], ); } Widget _buildDecorationGrid() { final items = [ _VipFeatureData( 'SVIP Badge', 'sc_images/vip/sc_vip_badge_$_assetLevel.png', ), _VipFeatureData('Frame', null, Icons.person_pin_circle_outlined), _VipFeatureData('Mount', null, Icons.auto_awesome_motion), _VipFeatureData('Mic Animation', null, Icons.radio_button_checked), _VipFeatureData('Entry Effect', null, Icons.airline_seat_recline_extra), ]; return Wrap( spacing: 10.w, runSpacing: 10.w, children: items .map( (item) => _buildFeatureTile( item, width: item.title == 'Entry Effect' ? 206.w : 98.w, height: 100.w, ), ) .toList(), ); } Widget _buildPrivilegeGrid() { final items = [ _VipFeatureData('Gif Profile\nPicture', null, Icons.badge_outlined), _VipFeatureData('Colorful\nNickname', null, Icons.credit_card), _VipFeatureData('Gif Room\nPicture', null, Icons.home_rounded), _VipFeatureData('Colorful ID', null, Icons.pin_rounded), ]; return Wrap( spacing: 34.w, runSpacing: 24.w, children: items.asMap().entries.map((entry) { final item = entry.value; return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => SCNavigatorUtils.push( context, '${VipRoute.benefit}?index=${entry.key}', ), child: SizedBox( width: 74.w, child: Column( children: [ Container( width: 46.w, height: 46.w, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white.withValues(alpha: 0.08), ), alignment: Alignment.center, child: Icon( item.icon, color: const Color(0xFFFF8E3D), size: 23.w, ), ), SizedBox(height: 7.w), Text( item.title, textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w400, height: 1.1, ), ), ], ), ), ); }).toList(), ); } Widget _buildRewardPlaceholder(IconData icon) { return Center( child: Container( width: 46.w, height: 46.w, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ const Color(0xFFFFD155).withValues(alpha: 0.28), Colors.white.withValues(alpha: 0.06), ], ), border: Border.all( color: const Color(0xFFFFD155).withValues(alpha: 0.45), width: 0.8.w, ), ), alignment: Alignment.center, child: Icon(icon, color: const Color(0xFFFFD155), size: 24.w), ), ); } Widget _buildWideRewardPlaceholder() { return Container( width: double.infinity, height: 40.w, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.w), gradient: LinearGradient( colors: [ const Color(0xFFFFD155).withValues(alpha: 0.14), const Color(0xFF7F4DFF).withValues(alpha: 0.16), const Color(0xFFFFD155).withValues(alpha: 0.14), ], ), border: Border.all( color: const Color(0xFFFFD155).withValues(alpha: 0.35), width: 0.8.w, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ for (int i = 0; i < 5; i++) Container( width: 4.w, height: 4.w, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white.withValues(alpha: 0.45), ), ), ], ), ); } Widget _buildFeatureTile( _VipFeatureData item, { required double width, required double height, }) { final isWide = item.title == 'Entry Effect'; return Container( width: width, height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6.w), border: Border.all(color: const Color(0xFFEBCB76), width: 1.w), color: Colors.black.withValues(alpha: 0.38), ), padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.w), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (item.assetPath != null) Expanded(child: Image.asset(item.assetPath!, fit: BoxFit.contain)) else Expanded( child: isWide ? Center(child: _buildWideRewardPlaceholder()) : _buildRewardPlaceholder( item.icon ?? Icons.auto_awesome, ), ), SizedBox(height: 5.w), Text( item.title, maxLines: 2, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w400, ), ), ], ), ); } Widget _buildActivateBar() { final bottomInset = MediaQuery.of(context).padding.bottom; final actionButton = _actionButtonConfigForLevel(_assetLevel); final barOverflow = 20.w + bottomInset; return SizedBox( height: 106.5.w, width: double.infinity, child: Stack( clipBehavior: Clip.none, children: [ Positioned( left: 0, right: 0, top: 0, bottom: -barOverflow, child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(20.w), topRight: Radius.circular(20.w), ), child: Image.asset( 'sc_images/vip/sc_vip_card_bg_$_assetLevel.png', fit: BoxFit.fitWidth, ), ), ), Positioned.fill( child: Padding( padding: EdgeInsets.fromLTRB( 30.w, 30.w, 30.w, 10.w + bottomInset, ), child: Row( children: [ Container( width: 22.w, height: 22.w, decoration: const BoxDecoration( shape: BoxShape.circle, color: Color(0xFFFFA629), ), alignment: Alignment.center, child: Icon( Icons.stars_rounded, color: Colors.white, size: 14.w, ), ), SizedBox(width: 6.w), Text( '30.5 M / 30 Days', style: TextStyle( color: const Color(0xFFFFE8A7), fontSize: 16.sp, fontWeight: FontWeight.w700, ), ), const Spacer(), Container( width: 106.w, height: 42.w, alignment: Alignment.center, decoration: BoxDecoration( image: DecorationImage( image: AssetImage(actionButton.backgroundAssetPath), fit: BoxFit.fill, ), ), child: Text( actionButton.label, textAlign: TextAlign.center, style: TextStyle( color: actionButton.textColor, fontFamily: actionButton.fontFamily, fontSize: actionButton.fontSize.sp, fontStyle: FontStyle.normal, fontWeight: FontWeight.w700, height: 1, ), ), ), ], ), ), ), ], ), ); } Widget _buildVipLevelMark(int level, {double scale = 1}) { return Transform.scale( scale: scale, alignment: Alignment.centerLeft, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( 'sc_images/vip/sc_vip_letter_v.png', width: 28.w, height: 24.7.w, fit: BoxFit.fill, ), Image.asset( 'sc_images/vip/sc_vip_letter_i.png', width: 14.w, height: 24.7.w, fit: BoxFit.fill, ), Image.asset( 'sc_images/vip/sc_vip_letter_p.png', width: 28.w, height: 24.7.w, fit: BoxFit.fill, ), SizedBox(width: 3.w), _buildVipLevelNumber(level), ], ), ); } Widget _buildVipLevelNumber(int level) { return Image.asset( 'sc_images/vip/sc_vip_number_${level}_filled.png', height: 25.w, fit: BoxFit.fill, color: const Color(0xFFFFD155), colorBlendMode: BlendMode.srcIn, ); } _VipOrnamentLayout _ornamentLayoutForLevel(int level) { switch (level) { case 1: return const _VipOrnamentLayout( width: 176.5, height: 36, top: -10, dx: 0, ); case 2: return const _VipOrnamentLayout( width: 246, height: 61, top: -20, dx: 0, ); case 3: return const _VipOrnamentLayout( width: 277, height: 79.5, top: -35, dx: 0, ); case 4: return const _VipOrnamentLayout( width: 240.5, height: 79.5, top: -35, dx: 0, ); case 5: default: return const _VipOrnamentLayout( width: 282.5, height: 101.5, top: -48, dx: 0, ); } } _VipActionButtonConfig _actionButtonConfigForLevel(int level) { switch (level) { case 1: case 2: case 3: case 4: case 5: default: return _showUpdateButton ? const _VipActionButtonConfig.update() : const _VipActionButtonConfig.activate(); } } int get _assetLevel => _selectedLevel.clamp(1, 5).toInt(); } class _VipOrnamentLayout { const _VipOrnamentLayout({ required this.width, required this.height, required this.top, required this.dx, }); final double width; final double height; final double top; final double dx; } class _VipFeatureData { const _VipFeatureData(this.title, this.assetPath, [this.icon]); final String title; final String? assetPath; final IconData? icon; } class _VipActionButtonConfig { const _VipActionButtonConfig({ required this.label, required this.backgroundAssetPath, required this.textColor, required this.fontSize, required this.fontFamily, }); const _VipActionButtonConfig.activate() : this( label: 'Activate', backgroundAssetPath: 'sc_images/vip/sc_vip_activate_button_bg.png', textColor: const Color(0xFF7B4B16), fontSize: 14, fontFamily: null, ); const _VipActionButtonConfig.update() : this( label: 'Update', backgroundAssetPath: 'sc_images/vip/sc_vip_update_button_bg.png', textColor: const Color(0xFF740000), fontSize: 16, fontFamily: 'Source Han Sans SC', ); final String label; final String backgroundAssetPath; final Color textColor; final double fontSize; final String? fontFamily; }