import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:keyboard/base/base_page.dart'; import 'package:keyboard/module/character_custom/character_custom_controller.dart'; import 'package:keyboard/resource/string.gen.dart'; import 'package:keyboard/utils/toast_util.dart'; import '../../resource/assets.gen.dart'; import '../../utils/styles.dart'; import '../../widget/auto_scroll_list_view.dart'; class CharacterCustomPage extends BasePage { const CharacterCustomPage({super.key}); static start() { return Get.to(() => CharacterCustomPage()); } @override bool immersive() { return true; } @override bool statusBarDarkFont() { return false; } @override Widget buildBody(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) { return; } controller.clickBack(); }, child: Obx(() { if (controller.currentStep.value == StepType.home) { return _buildCustomHomePage(); } else { return _buildStepsPage(); } }), ); } // 定制首页 Widget _buildCustomHomePage() { return Stack( children: [ Assets.images.bgCharacterCustomHuman.image( width: double.infinity, fit: BoxFit.fill, ), SafeArea( child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: EdgeInsets.only(left: 16.w), child: GestureDetector( onTap: () { controller.clickBack(); }, child: Assets.images.iconCharacterCustomClose.image( width: 24.w, height: 24.w, ), ), ), GestureDetector( onTap: () { controller.clickHistory(); }, child: Container( width: 76.r, height: 32.h, decoration: ShapeDecoration( gradient: LinearGradient( colors: [ const Color(0xFF702E96), const Color(0XFF400264), ], begin: Alignment.centerLeft, end: Alignment.centerRight, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(16.r), bottomLeft: Radius.circular(16.r), ), ), ), child: Center( child: Text( StringName.characterCustomHistory, style: TextStyle( color: Colors.white, fontSize: 14.sp, fontWeight: FontWeight.w400, ), ), ), ), ), ], ), ), Positioned( bottom: 50.h, left: 0, right: 0, child: GestureDetector( onTap: () { controller.clickNextButton(StepType.hobbies); }, child: Center( child: Assets.images.iconCharacterCustomButton.image( width: 234.w, fit: BoxFit.contain, ), ), ), ), ], ); } Widget _buildStepsPage() { return Stack( children: [ Assets.images.bgCharacterCustomSteps.image( width: double.infinity, fit: BoxFit.fill, ), SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only(left: 16.w, bottom: 48.h), child: GestureDetector( onTap: () { controller.clickBack(); }, child: Assets.images.iconCharacterCustomClose.image( width: 24.w, height: 24.w, ), ), ), Expanded( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ controller.currentStep.value == StepType.hobbies ? Assets.images.iconCharacterCustomStepOneTitle.image( fit: BoxFit.cover, width: 209.w, ) : controller.currentStep.value == StepType.characters ? Assets.images.iconCharacterCustomStepTwoTitle.image( fit: BoxFit.cover, width: 168.w, ) : Assets.images.iconCharacterCustomStepThreeTitle .image(fit: BoxFit.cover, width: 207.w), Container( margin: EdgeInsets.only( left: 16.w, right: 16.w, top: 24.h, ), width: double.infinity, decoration: ShapeDecoration( color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.r), ), shadows: [ BoxShadow( color: Color(0x66D788FF), blurRadius: 10.r, offset: Offset(0, 0), spreadRadius: 0, ), ], ), child: Column( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 26.w), decoration: BoxDecoration( image: DecorationImage( image: Assets.images.bgCharacterCustomStepsDesc .provider(), fit: BoxFit.fill, ), ), child: Text( StringName.characterCustomStepsDesc, style: TextStyle( color: const Color(0xFFAD88EB), fontSize: 12.sp, fontWeight: FontWeight.w400, ), ), ), SizedBox(height: 24.h), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( '第${controller.currentStep.value.value}步', style: TextStyle( color: const Color(0xFF755AAB), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), Text( " | ", style: TextStyle( color: const Color(0xFF755AAB), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), Opacity( opacity: 0.60, child: Text( '共3步', style: TextStyle( color: const Color(0xFF755BAB), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ), ], ), if (controller.currentStep.value == StepType.hobbies) _buildHobbiesPage(), if (controller.currentStep.value == StepType.characters) _buildCharacterPage(), if (controller.currentStep.value == StepType.inputName) _buildInputNamePage(), ], ), ), ], ), ), ), ], ), ), ], ); } Widget _buildSelectionPage({ required String title, required String subtitle, required List items, required RxList selectedLabels, required Function(dynamic) onSelected, required bool isCustomEnabled, required VoidCallback onCustomClick, required VoidCallback nextClick, required bool isShowEmoji, }) { return Obx(() { int rowCount = 3; // 3 行 int columnCount = (items.length / rowCount).ceil(); // 计算列数 return Column( children: [ Padding( padding: EdgeInsets.only(top: 15.h), child: Text( title, style: TextStyle( color: const Color(0xFF755BAB), fontSize: 18.sp, fontWeight: FontWeight.w500, ), ), ), Text( subtitle, style: TextStyle( color: const Color(0xFF755BAB).withOpacity(0.6), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), Container( margin: EdgeInsets.only(top: 32.h, left: 21.w, right: 21.w), height: 180.h, child: AutoScrollListView( itemCount: columnCount, // 列数 scrollDirection: Axis.horizontal, itemBuilder: (context, columnIndex) { // 3 个 item int startIndex = columnIndex * rowCount; List columnItems = items.skip(startIndex).take(rowCount).toList(); return Column( mainAxisSize: MainAxisSize.min, children: columnItems.map((item) { final emoji = item.emoji ?? ""; final name = item.name ?? ""; return Padding( padding: EdgeInsets.symmetric( vertical: 4.h, horizontal: 4.w, ), child: Obx(() { bool isSelected = selectedLabels.any( (selected) => selected.name == item.name, ); return ChoiceChip( label: Text( isShowEmoji ? "$emoji$name" : name, style: TextStyle( color: isSelected ? Colors.white : const Color(0xFF755BAB), fontSize: 14.sp, fontWeight: FontWeight.w400, ), ), showCheckmark: false, selected: isSelected, selectedColor: const Color(0xFFB782FF), backgroundColor: Colors.white, shape: RoundedRectangleBorder( side: BorderSide( width: 1.w, color: const Color(0x4C755BAB), ), borderRadius: BorderRadius.circular(31.r), ), onSelected: (selected) { onSelected(item); }, ); }), ); }).toList(), ); }, ), ), Visibility( visible: isCustomEnabled, replacement: SizedBox(width: 115.w, height: 36.h), child: GestureDetector( onTap: onCustomClick, child: Container( margin: EdgeInsets.only(top: 3.h), child: DottedBorder( color: const Color(0xFFC9C2DB), strokeWidth: 1.0.w, borderType: BorderType.RRect, radius: Radius.circular(20.r), child: Container( width: 115.w, height: 33.h, alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Assets.images.iconCharacterCustomPlus.image( width: 18.w, height: 18.w, ), Text( StringName.characterCustomCustomizable, style: TextStyle( color: const Color(0xFFC9C2DB), fontSize: 14.sp, fontWeight: FontWeight.w500, ), ), ], ), ), ), ), ), ), Container( margin: EdgeInsets.only(top: 107.h, bottom: 32.h), child: _buildNextButton( isEnable: selectedLabels.isNotEmpty, onTap: nextClick, ), ), ], ); }); } Widget _buildNextButton({required VoidCallback onTap, required isEnable}) { return GestureDetector( onTap: () { onTap(); }, child: Container( width: 220.w, height: 48.h, decoration: isEnable ? Styles.getActivateButtonDecoration(31.r) : Styles.getInactiveButtonDecoration(31.r), child: Center( child: Text( '下一步', style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w500, ), ), ), ), ); } // 选择爱好页面 Widget _buildHobbiesPage() { return _buildSelectionPage( title: StringName.characterCustomHobbiesTitle, subtitle: "(最多选择${controller.currentCharacterCustomConfig?.maxHobbyNum ?? 3}个)", items: controller.hobbiesLabelsList, selectedLabels: controller.hobbiesSelectLabels, isShowEmoji: true, onSelected: (name) { controller.selectHobby(name); }, isCustomEnabled: controller.currentCharacterCustomConfig?.customHobby == true, onCustomClick: () { controller.clickHobbiesCustom(); }, nextClick: () { controller.clickHobbiesNext(); }, ); } // 选择性格页面 Widget _buildCharacterPage() { return _buildSelectionPage( title: StringName.characterCustomcharacterTitle, subtitle: "(最多选择${controller.currentCharacterCustomConfig?.maxCharacterNum ?? 3}个)", items: controller.characterLabelsList, selectedLabels: controller.characterSelectLabels, isShowEmoji: true, onSelected: (character) { controller.selectCharacter(character); }, isCustomEnabled: controller.currentCharacterCustomConfig?.customCharacter == true, onCustomClick: () { controller.clickCharacterCustom(); }, nextClick: () { controller.clickCharacterNext(); }, ); } // 输入名字页面 Widget _buildInputNamePage() { return Obx(() { return Column( children: [ Padding( padding: EdgeInsets.only(top: 15.h), child: Text( StringName.characterCustomNameTitle, style: TextStyle( color: const Color(0xFF755BAB), fontSize: 18.sp, fontWeight: FontWeight.w500, ), ), ), Text( "人设名称(最多5个字)", style: TextStyle( color: Color(0xFF755BAB).withValues(alpha: 0.6), fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), Container( margin: EdgeInsets.only(top: 32.h, left: 21.w, right: 21.w), alignment: Alignment.center, child: Container( height: 48.h, alignment: Alignment.center, decoration: ShapeDecoration( color: const Color(0xFFF5F4F9), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(31.r), ), ), child: TextField( // maxLength: maxLength, maxLines: null, expands: true, textAlign: TextAlign.center, textAlignVertical: TextAlignVertical.center, onChanged: (value) { controller.currentNameValue.value = value; }, decoration: InputDecoration( counterText: "", hintText: StringName.characterCustomNameHint, hintStyle: TextStyle(color: Colors.black.withAlpha(66)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(31.r), borderSide: BorderSide.none, // 移除边框线 ), filled: true, fillColor: const Color(0xFFF5F4F9), ), ), ), ), Container( margin: EdgeInsets.only(top: 44.h, bottom: 32.h), child: _buildNextButton( isEnable: controller.currentNameValue.trim().isNotEmpty, onTap: () { controller.clickInputNameNext(); }, ), ), ], ); }); } }