chatapp3-flutter/lib/modules/room/background/room_background_select_page.dart
2026-04-22 20:08:45 +08:00

413 lines
13 KiB
Dart

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<RoomBackgroundSelectPage> createState() =>
_RoomBackgroundSelectPageState();
}
class _RoomBackgroundSelectPageState extends State<RoomBackgroundSelectPage>
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<void> _openUploadPage() async {
final uploadedPath = await VoiceRoomRoute.openRoomBackgroundUpload<String>(
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<Color>(
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<int> 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;
}