import 'dart:io'; 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_localizations.dart'; import 'package:yumi/modules/room/voice_room_route.dart'; import 'package:yumi/shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart'; import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart'; import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; class RoomBackgroundSelectPage extends StatefulWidget { const RoomBackgroundSelectPage({super.key}); @override State createState() => _RoomBackgroundSelectPageState(); } class _RoomBackgroundSelectPageState extends State with SingleTickerProviderStateMixin { late final TabController _tabController; late final List<_RoomBackgroundItem> _officialItems; late final List<_RoomBackgroundItem> _mineItems; bool get _showAddButton => _tabController.index == 1; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); _tabController.addListener(_handleTabChange); _officialItems = [ _RoomBackgroundItem(inUse: true), _RoomBackgroundItem(), _RoomBackgroundItem(), _RoomBackgroundItem(), ]; _mineItems = [_RoomBackgroundItem(), _RoomBackgroundItem()]; } @override void dispose() { _tabController.removeListener(_handleTabChange); _tabController.dispose(); super.dispose(); } void _handleTabChange() { if (!mounted) { return; } setState(() {}); } Future _openUploadPage() async { final uploadedPath = await VoiceRoomRoute.openRoomBackgroundUpload( context, ); if (!mounted || (uploadedPath ?? "").trim().isEmpty) { return; } setState(() { for (final item in _mineItems) { item.inUse = false; } _mineItems.insert( 0, _RoomBackgroundItem(localImagePath: uploadedPath, inUse: true), ); }); _tabController.animateTo(1); } void _selectItem(List<_RoomBackgroundItem> items, int index) { setState(() { for (final item in items) { item.inUse = false; } items[index].inUse = true; }); } @override Widget build(BuildContext context) { final localizations = SCAppLocalizations.of(context)!; final bottomPadding = MediaQuery.of(context).padding.bottom; return Stack( children: [ Image.asset( SCGlobalConfig.businessLogicStrategy.getLanguagePageBackgroundImage(), width: ScreenUtil().screenWidth, height: ScreenUtil().screenHeight, fit: BoxFit.fill, ), Scaffold( backgroundColor: Colors.transparent, appBar: _buildAppBar(context, localizations), body: Container( color: const Color(0xff0F0F0F), child: Stack( children: [ Column( children: [ Container( height: 0.5, color: Colors.white.withValues(alpha: 0.15), ), Expanded( child: TabBarView( controller: _tabController, children: [ _RoomBackgroundGrid( items: _officialItems, onTap: (index) => _selectItem(_officialItems, index), bottomPadding: 24.w, ), _RoomBackgroundGrid( items: _mineItems, onTap: (index) => _selectItem(_mineItems, index), bottomPadding: 100.w, ), ], ), ), ], ), if (_showAddButton) Positioned( left: 18.w, right: 18.w, bottom: 20.w + bottomPadding, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: _openUploadPage, child: Container( height: 48.w, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(999.w), gradient: LinearGradient( colors: [ SocialChatTheme.primaryLight, const Color(0xff8BF2D0), ], ), ), child: Text( 'add', style: TextStyle( color: const Color(0xff0B2823), fontSize: 16.sp, fontWeight: FontWeight.w700, ), ), ), ), ), ], ), ), ), ], ); } PreferredSizeWidget _buildAppBar( BuildContext context, SCAppLocalizations localizations, ) { return SocialChatAppBar( backgroundColor: Colors.transparent, child: Row( children: [ SizedBox( width: 52.w, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => Navigator.pop(context), child: Icon( SCGlobalConfig.lang == "ar" ? Icons.keyboard_arrow_right : Icons.keyboard_arrow_left, size: 28.w, color: Colors.white, ), ), ), Expanded( child: Center( child: SizedBox( width: 180.w, child: TabBar( controller: _tabController, labelColor: Colors.white, unselectedLabelColor: Colors.white54, overlayColor: const WidgetStatePropertyAll( Colors.transparent, ), labelStyle: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), unselectedLabelStyle: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, ), indicator: SCFixedWidthTabIndicator( width: 18.w, color: SocialChatTheme.primaryLight, height: 3.w, borderRadius: 2.w, ), dividerColor: Colors.transparent, splashFactory: NoSplash.splashFactory, tabs: [ Tab(text: localizations.official), Tab(text: localizations.mine), ], ), ), ), ), SizedBox( width: 72.w, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: _openUploadPage, child: Align( alignment: Alignment.center, child: Text( localizations.custom, style: TextStyle( color: SocialChatTheme.primaryLight, fontSize: 15.sp, fontWeight: FontWeight.w600, ), ), ), ), ), ], ), ); } } class _RoomBackgroundGrid extends StatelessWidget { const _RoomBackgroundGrid({ required this.items, required this.onTap, required this.bottomPadding, }); final List<_RoomBackgroundItem> items; final ValueChanged onTap; final double bottomPadding; @override Widget build(BuildContext context) { return GridView.builder( padding: EdgeInsets.fromLTRB(16.w, 16.w, 16.w, bottomPadding), itemCount: items.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 14.w, crossAxisSpacing: 14.w, childAspectRatio: 0.64, ), itemBuilder: (context, index) { return _RoomBackgroundCard( item: items[index], onTap: () => onTap(index), ); }, ); } } class _RoomBackgroundCard extends StatelessWidget { const _RoomBackgroundCard({required this.item, required this.onTap}); final _RoomBackgroundItem item; final VoidCallback onTap; @override Widget build(BuildContext context) { final localizations = SCAppLocalizations.of(context)!; return GestureDetector( onTap: onTap, child: Container( decoration: BoxDecoration( color: const Color(0xff18F2B1).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12.w), border: Border.all( color: item.inUse ? SocialChatTheme.primaryLight : Colors.white.withValues(alpha: 0.08), width: 1.w, ), ), child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded( child: Padding( padding: EdgeInsets.all(10.w), child: ClipRRect( borderRadius: BorderRadius.circular(10.w), child: _buildPreview(context), ), ), ), Padding( padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 12.w), child: Text( localizations.staticOrGifImage, textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.white, fontSize: 13.sp, fontWeight: FontWeight.w500, ), ), ), ], ), if (item.inUse) PositionedDirectional( top: 10.w, start: 10.w, child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.w), decoration: BoxDecoration( color: SocialChatTheme.primaryLight, borderRadius: BorderRadius.circular(999.w), ), child: Text( localizations.inUse, style: TextStyle( color: const Color(0xff09372E), fontSize: 11.sp, fontWeight: FontWeight.w700, ), ), ), ), ], ), ), ); } Widget _buildPreview(BuildContext context) { final localizations = SCAppLocalizations.of(context)!; if ((item.localImagePath ?? "").isNotEmpty) { final file = File(item.localImagePath!); if (file.existsSync()) { return Image.file(file, fit: BoxFit.cover); } } return DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [const Color(0xff0F4C40), const Color(0xff0B2823)], ), ), child: Stack( children: [ Center( child: Icon( Icons.image_outlined, size: 34.w, color: Colors.white.withValues(alpha: 0.45), ), ), Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 12.w), child: Text( localizations.staticOrGifImage, textAlign: TextAlign.center, style: TextStyle( color: Colors.white.withValues(alpha: 0.72), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ), ), ], ), ); } } class _RoomBackgroundItem { _RoomBackgroundItem({this.localImagePath, this.inUse = false}); final String? localImagePath; bool inUse; }