import 'package:aslan/chatvibe_core/utilities/at_loading_manager.dart'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:aslan/chatvibe_ui/components/at_debounce_widget.dart'; import 'package:marquee/marquee.dart'; import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import '../../../../app_localizations.dart'; import '../../../../chatvibe_core/constants/at_global_config.dart'; import '../../../../chatvibe_core/routes/at_fluro_navigator.dart'; import '../../../../chatvibe_core/utilities/at_banner_utils.dart'; import '../../../../chatvibe_data/models/enum/at_vip_type.dart'; import '../../../../chatvibe_data/sources/repositories/room_repository_imp.dart'; import '../../../../chatvibe_domain/models/res/at_banner_leaderboard_res.dart'; import '../../../../chatvibe_domain/models/res/at_index_banner_res.dart'; import '../../../../chatvibe_domain/models/res/country_res.dart'; import '../../../../chatvibe_domain/models/res/follow_room_res.dart'; import '../../../../chatvibe_domain/models/res/room_res.dart'; import '../../../../chatvibe_managers/app_general_manager.dart'; import '../../../../chatvibe_managers/rtc_manager.dart'; import '../../../../chatvibe_ui/components/at_compontent.dart'; import '../../../../chatvibe_ui/components/text/at_text.dart'; import '../../../../chatvibe_ui/widgets/country/at_country_flag_image.dart'; import '../../../index/main_route.dart'; class HomePartyPage extends StatefulWidget { @override _HomePartyPageState createState() => _HomePartyPageState(); } class _HomePartyPageState extends State with SingleTickerProviderStateMixin { List historyRooms = []; String? lastId; bool isLoading = false; final RefreshController _refreshController = RefreshController( initialRefresh: false, ); List rooms = []; int _currentIndex = 0; final ScrollController _countryTabController = ScrollController(); final List _countryTabKeys = []; List _countries = []; Country? _selectedCountry; int _dataRequestToken = 0; bool _countryLoading = false; @override void initState() { super.initState(); loadCountries(); loadData(); } @override void dispose() { _countryTabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, body: Stack( alignment: AlignmentDirectional.bottomCenter, children: [ SmartRefresher( enablePullDown: true, enablePullUp: false, controller: _refreshController, onRefresh: () { loadData(); }, onLoading: () {}, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ Selector>( selector: (_, provider) => List.unmodifiable( provider.exploreBanners, ), builder: (context, exploreBanners, child) { return _banner(exploreBanners); }, ), Selector( selector: (_, provider) => provider.appLeaderResult, builder: (context, appLeaderResult, child) { return _banner2(appLeaderResult); }, ), _buildCountryTabs(), rooms.isEmpty ? GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { loadData(); }, child: isLoading ? Center(child: CupertinoActivityIndicator()) : mainEmpty( msg: ATAppLocalizations.of(context)!.noData, ), ) : ListView.separated( shrinkWrap: true, padding: EdgeInsets.symmetric(vertical: 8.w), physics: const NeverScrollableScrollPhysics(), itemCount: rooms.length, itemBuilder: (context, index) { return _buildItem(rooms[index], index); }, separatorBuilder: (BuildContext context, int index) { return SizedBox(height: 4.w); }, ), ], ), ), ), ], ), ); } _banner(List exploreBanners) { return Stack( alignment: AlignmentDirectional.bottomCenter, children: [ Container( margin: EdgeInsets.symmetric(horizontal: 17.w), child: CarouselSlider( options: CarouselOptions( height: 98.w, autoPlay: true, // 启用自动播放 enlargeCenterPage: false, // 居中放大当前页面 enableInfiniteScroll: true, // 启用无限循环 autoPlayAnimationDuration: Duration(milliseconds: 800), // 自动播放动画时长 viewportFraction: 1, // 视口分数 onPageChanged: (index, reason) { _currentIndex = index; setState(() {}); }, ), items: exploreBanners.map((item) { return GestureDetector( child: netImage( url: item.cover ?? "", height: 98.w, width: ScreenUtil().screenWidth * 0.9, fit: BoxFit.contain, borderRadius: BorderRadius.circular(12.w), ), onTap: () { print('ads:${item.toJson()}'); ATBannerUtils.openBanner(item, context); }, ); }).toList(), ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: exploreBanners.asMap().entries.map((entry) { return Container( width: 6.0, height: 6.0, margin: EdgeInsets.symmetric(vertical: 15.0, horizontal: 4.0), decoration: BoxDecoration( shape: BoxShape.circle, color: _currentIndex == entry.key ? Colors.blue : Colors.white, ), ); }).toList(), ), ], ); } _banner2(ATBannerLeaderboardRes? appLeaderResult) { String avatarAt(List? users, int index) { if (users == null || users.length <= index) return ""; return users[index].avatar ?? ""; } final roomGiftWeekly = appLeaderResult?.roomGiftsLeaderboard?.weekly; final wealthWeekly = appLeaderResult?.giftsSendLeaderboard?.weekly; final charmWeekly = appLeaderResult?.giftsReceivedLeaderboard?.weekly; final roomGiftOneAvatar = avatarAt(roomGiftWeekly, 0); final roomGiftTwoAvatar = avatarAt(roomGiftWeekly, 1); final roomGiftThreeAvatar = avatarAt(roomGiftWeekly, 2); final wealthOneAvatar = avatarAt(wealthWeekly, 0); final wealthTwoAvatar = avatarAt(wealthWeekly, 1); final wealthThreeAvatar = avatarAt(wealthWeekly, 2); final charmOneAvatar = avatarAt(charmWeekly, 0); final charmTwoAvatar = avatarAt(charmWeekly, 1); final charmThreeAvatar = avatarAt(charmWeekly, 2); return SizedBox( height: 120.w, child: Row( children: [ Expanded( child: GestureDetector( child: Stack( alignment: AlignmentDirectional.center, children: [ PositionedDirectional( start: 30.w, top: 63.w, child: head(url: wealthTwoAvatar, width: 28.w), ), Positioned( top: 35.w, child: head(url: wealthOneAvatar, width: 36.w), ), PositionedDirectional( end: 30.w, top: 63.w, child: head(url: wealthThreeAvatar, width: 28.w), ), Image.asset( ATGlobalConfig.businessLogicStrategy .getPopularLeaderboardBackgroundImage('wealth'), fit: BoxFit.fill, ), ], ), onTap: () { ATNavigatorUtils.push( context, "${MainRoute.webViewPage}?url=${Uri.encodeComponent(ATGlobalConfig.wealthRankUrl)}&showTitle=false", replace: false, ); }, ), ), Expanded( child: GestureDetector( child: Stack( alignment: AlignmentDirectional.center, children: [ PositionedDirectional( start: 28.w, top: 68.w, child: head(url: roomGiftTwoAvatar, width: 28.w), ), Positioned( top: 35.w, child: head(url: roomGiftOneAvatar, width: 38.w), ), PositionedDirectional( end: 29.w, top: 68.w, child: head(url: roomGiftThreeAvatar, width: 28.w), ), Image.asset( ATGlobalConfig.businessLogicStrategy .getPopularLeaderboardBackgroundImage('room'), fit: BoxFit.fill, ), ], ), onTap: () { ATNavigatorUtils.push( context, "${MainRoute.webViewPage}?url=${Uri.encodeComponent(ATGlobalConfig.roomRankUrl)}&showTitle=false", replace: false, ); }, ), ), Expanded( child: GestureDetector( child: Stack( alignment: AlignmentDirectional.center, children: [ PositionedDirectional( start: 28.w, top: 66.w, child: head(url: charmTwoAvatar, width: 28.w), ), Positioned( top: 38.w, child: head(url: charmOneAvatar, width: 37.w), ), PositionedDirectional( end: 31.w, top: 66.w, child: head(url: charmThreeAvatar, width: 28.w), ), Image.asset( ATGlobalConfig.businessLogicStrategy .getPopularLeaderboardBackgroundImage('charm'), fit: BoxFit.fill, ), ], ), onTap: () { ATNavigatorUtils.push( context, "${MainRoute.webViewPage}?url=${Uri.encodeComponent(ATGlobalConfig.charmRankUrl)}&showTitle=false", replace: false, ); }, ), ), ], ), ); } Widget _buildCountryTabs() { _syncCountryTabKeys(); return Container( height: 32.w, margin: EdgeInsetsDirectional.only(start: 18.w, end: 18.w, top: 4.w), child: Row( children: [ Expanded( child: SingleChildScrollView( controller: _countryTabController, scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), child: Row( children: [ KeyedSubtree(key: _countryTabKeys[0], child: _buildHotTab()), ...List.generate(_countries.length, (index) { final country = _countries[index]; return KeyedSubtree( key: _countryTabKeys[index + 1], child: _buildCountryTab(country, index + 1), ); }), ], ), ), ), SizedBox(width: 8.w), ATDebounceWidget( onTap: _showCountrySheet, child: Container( alignment: Alignment.center, height: 23.w, width: 23.w, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: const Color(0xff000000), width: 1.w), ), child: Icon( Icons.keyboard_arrow_down_rounded, size: 18.w, color: const Color(0xff333333), ), ), ), ], ), ); } Widget _buildHotTab() { final isSelected = _selectedCountry == null; return Padding( padding: EdgeInsetsDirectional.only(end: 6.w), child: ATDebounceWidget( onTap: () => _selectCountry(null, 0), child: Container( height: 32.w, padding: EdgeInsetsDirectional.symmetric(horizontal: 13.w), decoration: _countryTabDecoration(isSelected), child: Row( mainAxisSize: MainAxisSize.min, children: [ Image.asset( "atu_images/index/at_icon_fire_department.png", height: 19.w, ), SizedBox(width: 3.w), text( ATAppLocalizations.of(context)!.hot, textColor: const Color(0xff333333), fontSize: 14.sp, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, lineHeight: 1.1, ), ], ), ), ), ); } Widget _buildCountryTab(Country country, int tabIndex) { final isSelected = _isSameCountry(_selectedCountry, country); return Padding( padding: EdgeInsetsDirectional.only(end: 6.w), child: ATDebounceWidget( onTap: () => _selectCountry(country, tabIndex), child: Container( height: 32.w, constraints: BoxConstraints(maxWidth: 144.w), padding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), decoration: _countryTabDecoration(isSelected), child: Row( mainAxisSize: MainAxisSize.min, children: [ _buildCountryFlag(country, width: 22.w, height: 16.w), SizedBox(width: 6.w), Flexible( child: text( _countryDisplayName(country), maxLines: 1, overflow: TextOverflow.ellipsis, textColor: isSelected ? const Color(0xff333333) : const Color(0xff333333), fontSize: 14.sp, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, lineHeight: 1.1, ), ), ], ), ), ), ); } BoxDecoration _countryTabDecoration(bool isSelected) { return BoxDecoration( color: isSelected ? null : const Color(0xffeeeeee), gradient: isSelected ? const LinearGradient( colors: [Color(0xffffd800), Color(0xffff9500)], begin: AlignmentDirectional.topCenter, end: AlignmentDirectional.bottomCenter, ) : null, borderRadius: BorderRadius.circular(40.w), ); } Widget _buildCountryFlag( Country country, { required double width, required double height, }) { final flag = country.nationalFlag ?? ""; if (flag.isEmpty) { return Container( width: width, height: height, decoration: BoxDecoration( color: const Color(0xffd9d9d9), borderRadius: BorderRadius.circular(3.w), ), ); } return netImage( url: flag, width: width, height: height, fit: BoxFit.cover, borderRadius: BorderRadius.circular(3.w), noDefaultImg: true, ); } void _showCountrySheet() { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, barrierColor: const Color(0x73000000), isScrollControlled: true, builder: (sheetContext) { return SafeArea( top: false, child: Container( constraints: BoxConstraints( maxHeight: ScreenUtil().screenHeight * 0.6, ), padding: EdgeInsetsDirectional.only( start: 20.w, end: 20.w, top: 20.w, bottom: MediaQuery.of(sheetContext).padding.bottom + 20.w, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadiusDirectional.vertical( top: Radius.circular(20.w), ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ text( ATAppLocalizations.of(context)!.selectCountry, textColor: const Color(0xff333333), fontWeight: FontWeight.w700, lineHeight: 1.1, fontSize: 18.sp, ), SizedBox(height: 20.w), Flexible( child: _countries.isEmpty ? Center( child: _countryLoading ? const CupertinoActivityIndicator() : mainEmpty( msg: ATAppLocalizations.of( context, )!.noData, ), ) : GridView.builder( shrinkWrap: true, padding: EdgeInsets.zero, physics: const BouncingScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 12.w, crossAxisSpacing: 8.w, childAspectRatio: 111 / 30, ), itemCount: _countries.length, itemBuilder: (context, index) { final country = _countries[index]; return _buildCountrySheetItem( country, index + 1, sheetContext, ); }, ), ), ], ), ), ); }, ); } Widget _buildCountrySheetItem( Country country, int tabIndex, BuildContext sheetContext, ) { final isSelected = _isSameCountry(_selectedCountry, country); return ATDebounceWidget( onTap: () { Navigator.of(sheetContext).pop(); _selectCountry(country, tabIndex); }, child: Container( height: 30.w, padding: EdgeInsetsDirectional.symmetric(horizontal: 10.w), decoration: BoxDecoration( color: isSelected ? null : const Color(0xfff2f2f2), gradient: isSelected ? const LinearGradient( colors: [Color(0xffffd800), Color(0xffff9500)], begin: AlignmentDirectional.topCenter, end: AlignmentDirectional.bottomCenter, ) : null, borderRadius: BorderRadius.circular(8.w), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildCountryFlag(country, width: 18.w, height: 12.w), SizedBox(width: 6.w), Flexible( child: Text( _countryDisplayName(country), maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: const Color(0xff333333), fontSize: 13.sp, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, decoration: TextDecoration.none, height: 1.1, ), ), ), ], ), ), ); } void _selectCountry(Country? country, int tabIndex) { if (_isSameCountry(_selectedCountry, country)) { _scrollCountryTabToIndex(tabIndex); return; } setState(() { _selectedCountry = country; rooms = []; }); _scrollCountryTabToIndex(tabIndex); loadData(); } void _scrollCountryTabToIndex(int tabIndex) { if (tabIndex < 0 || tabIndex >= _countryTabKeys.length) return; WidgetsBinding.instance.addPostFrameCallback((_) { final tabContext = _countryTabKeys[tabIndex].currentContext; if (tabContext == null) return; Scrollable.ensureVisible( tabContext, duration: const Duration(milliseconds: 250), curve: Curves.easeOut, alignment: 0.5, ); }); } void _syncCountryTabKeys() { final total = _countries.length + 1; while (_countryTabKeys.length < total) { _countryTabKeys.add(GlobalKey()); } if (_countryTabKeys.length > total) { _countryTabKeys.removeRange(total, _countryTabKeys.length); } } bool _isSameCountry(Country? left, Country? right) { if (left == null || right == null) return left == right; final leftKey = left.id ?? left.alphaTwo ?? left.countryName; final rightKey = right.id ?? right.alphaTwo ?? right.countryName; return leftKey == rightKey; } String _countryDisplayName(Country country) { return country.countryName ?? ""; } void loadCountries() { _countryLoading = true; ChatRoomRepository() .roomLiveVoiceCountry() .then((values) { if (!mounted) return; setState(() { _countries = values; _countryLoading = false; }); }) .catchError((_) { if (!mounted) return; setState(() { _countryLoading = false; }); }); } loadData() { final requestToken = ++_dataRequestToken; final selectedCountry = _selectedCountry; setState(() { isLoading = true; }); ATLoadingManager.exhibitOperation(); final Future> request = selectedCountry == null ? ChatRoomRepository().discovery() : ChatRoomRepository().roomLiveVoiceExplore( countryCode: selectedCountry.countryCode, ); request .then((values) { if (!mounted || requestToken != _dataRequestToken) return; rooms = values; isLoading = false; _refreshController.refreshCompleted(); _refreshController.loadComplete(); ATLoadingManager.veilRoutine(); if (mounted) setState(() {}); }) .catchError((e) { if (!mounted || requestToken != _dataRequestToken) return; _refreshController.loadNoData(); _refreshController.refreshCompleted(); ATLoadingManager.veilRoutine(); isLoading = false; if (mounted) setState(() {}); }); Provider.of(context, listen: false).loadMainBanner(); Provider.of(context, listen: false).appLeaderboard(); } String getRoomCoverHeaddress(ChatVibeRoomRes roomRes) { var vip = roomRes.userProfile?.getVIP(); if (vip?.name == ATVIPType.DUKE.name) { return "atu_images/vip/at_icon_vip4_room_cover_headdress.png"; } else if (vip?.name == ATVIPType.KING.name) { return "atu_images/vip/at_icon_vip5_room_cover_headdress.png"; } else if (vip?.name == ATVIPType.EMPEROR.name) { return "atu_images/vip/at_icon_vip6_room_cover_headdress.png"; } return ""; } _buildItem(ChatVibeRoomRes res, int index) { return ATDebounceWidget( child: SizedBox( height: 105.w, child: Stack( alignment: Alignment.center, children: [ Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(13.w), border: index < 3 ? null : Border.all( color: ATGlobalConfig.businessLogicStrategy .getExploreRoomBorderColor(), width: ATGlobalConfig.businessLogicStrategy .getExploreRoomBorderWidth() .w, ), ), margin: EdgeInsetsDirectional.symmetric( horizontal: 18.w, vertical: 5.w, ), child: Row( children: [ Stack( children: [ netImage( url: res.roomCover ?? "", fit: BoxFit.cover, borderRadius: BorderRadius.all(Radius.circular(12.w)), width: 95.w, height: 95.w, ), PositionedDirectional( top: 3.w, end: 5.w, child: (res.extValues?.roomSetting?.password?.isNotEmpty ?? false) ? Image.asset( "atu_images/index/at_icon_room_suo.png", width: 16.w, height: 16.w, ) : Container(), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 15.w), Row( children: [ SizedBox(width: 8.w), ATCountryFlagImage( countryName: res.countryName, width: 25.w, height: 15.w, borderRadius: BorderRadius.all( Radius.circular(3.w), ), ), SizedBox(width: 5.w), Container( constraints: BoxConstraints( maxHeight: 21.w, maxWidth: 150.w, ), child: (res.roomName?.length ?? 0) > ATGlobalConfig.businessLogicStrategy .getExploreRoomNameMarqueeThreshold() ? Marquee( text: res.roomName ?? "", style: TextStyle( fontSize: 15.sp, color: Colors.black, decoration: TextDecoration.none, ), scrollAxis: Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.start, blankSpace: 20.0, velocity: 40.0, pauseAfterRound: Duration(seconds: 1), accelerationDuration: Duration( seconds: 1, ), accelerationCurve: Curves.easeOut, decelerationDuration: Duration( milliseconds: 500, ), decelerationCurve: Curves.easeOut, ) : Text( res.roomName ?? "", maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15.sp, color: Colors.black, decoration: TextDecoration.none, ), ), ), ], ), SizedBox(height: 3.w), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(width: 8.w), text( "ID:${res.userProfile?.getID()}", fontWeight: FontWeight.w600, textColor: Colors.black, fontSize: 15.sp, ), ], ), SizedBox(height: 3.w), Container( constraints: BoxConstraints(maxWidth: 180.w), margin: EdgeInsetsDirectional.symmetric( horizontal: 8.w, ), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( mainAxisSize: MainAxisSize.min, spacing: 2.w, children: List.generate( (res.onlineUsers?.length ?? 0), (index) { return netImage( url: res.onlineUsers?[index].userAvatar ?? "", width: 23.w, height: 23.w, shape: BoxShape.circle, ); }, ), ), ), ), ], ), ], ), ), index < 3 ? IgnorePointer( child: Transform.translate( offset: Offset(0, -3), child: Image.asset( ATGlobalConfig.businessLogicStrategy .getExploreRankIconPattern(false, index + 1), fit: BoxFit.fill, ), ), ) : Container(), PositionedDirectional( end: 35.w, bottom: 25.w, child: Row( children: [ Image.asset( "atu_images/index/at_icon_room_flot_ani.gif", width: 14.w, height: 14.w, ), text( res.extValues?.memberQuantity ?? "0", textColor: Colors.black38, fontSize: 11.sp, ), ], ), ), ], ), ), onTap: () { Provider.of( context, listen: false, ).joinRoom(context, res.id ?? ""); }, ); } }