|
|
@@ -1,11 +1,14 @@
|
|
|
+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';
|
|
|
|
|
|
class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
const CharacterCustomPage({super.key});
|
|
|
@@ -26,10 +29,17 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
|
|
|
@override
|
|
|
Widget buildBody(BuildContext context) {
|
|
|
- return buildHappyPage();
|
|
|
+ return Obx(() {
|
|
|
+ if (controller.currentStep.value == StepType.home) {
|
|
|
+ return _buildCustomHomePage();
|
|
|
+ } else {
|
|
|
+ return _buildStepsPage();
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- Widget buildCustomHomePage() {
|
|
|
+ // 定制首页
|
|
|
+ Widget _buildCustomHomePage() {
|
|
|
return Stack(
|
|
|
children: [
|
|
|
Assets.images.bgCharacterCustomHuman.image(
|
|
|
@@ -69,7 +79,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
),
|
|
|
child: Center(
|
|
|
child: Text(
|
|
|
- '定制历史',
|
|
|
+ StringName.characterCustomHistory,
|
|
|
style: TextStyle(
|
|
|
color: Colors.white,
|
|
|
fontSize: 14.sp,
|
|
|
@@ -89,7 +99,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
right: 0,
|
|
|
child: GestureDetector(
|
|
|
onTap: () {
|
|
|
- controller.clickStartCustom();
|
|
|
+ controller.clickNextButton(StepType.hobbies);
|
|
|
},
|
|
|
child: Center(
|
|
|
child: Assets.images.iconCharacterCustomButton.image(
|
|
|
@@ -103,40 +113,48 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- Widget buildHappyPage() {
|
|
|
- return Container(
|
|
|
- child: 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),
|
|
|
- child: GestureDetector(
|
|
|
- onTap: () {
|
|
|
- controller.clickBack();
|
|
|
- },
|
|
|
- child: Assets.images.iconCharacterCustomClose.image(
|
|
|
- width: 24.w,
|
|
|
- height: 24.w,
|
|
|
- ),
|
|
|
+ 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(
|
|
|
+ ),
|
|
|
+
|
|
|
+ Expanded(
|
|
|
+ child: SingleChildScrollView(
|
|
|
child: Column(
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
children: [
|
|
|
-
|
|
|
- Assets.images.iconCharacterCustomStepOneTitle.image(
|
|
|
- fit: BoxFit.cover,
|
|
|
- width: 214.w,
|
|
|
- ),
|
|
|
+ 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,
|
|
|
@@ -185,7 +203,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
children: [
|
|
|
Text(
|
|
|
- '第1步',
|
|
|
+ '第${controller.currentStep.value.value}步',
|
|
|
style: TextStyle(
|
|
|
color: const Color(0xFF755AAB),
|
|
|
fontSize: 12.sp,
|
|
|
@@ -213,94 +231,312 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
|
|
|
),
|
|
|
],
|
|
|
),
|
|
|
+
|
|
|
+ if (controller.currentStep.value ==
|
|
|
+ StepType.hobbies)
|
|
|
+ _buildHobbiesPage(),
|
|
|
+ if (controller.currentStep.value ==
|
|
|
+ StepType.characters)
|
|
|
+ _buildCharacterPage(),
|
|
|
+ if (controller.currentStep.value ==
|
|
|
+ StepType.inputName)
|
|
|
+ _buildInputNamePage(),
|
|
|
],
|
|
|
),
|
|
|
),
|
|
|
- _buildInterestsPage(),
|
|
|
],
|
|
|
),
|
|
|
),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildSelectionPage({
|
|
|
+ required String title,
|
|
|
+ required String subtitle,
|
|
|
+ required RxList<dynamic> items,
|
|
|
+ required RxList<String> selectedLabels,
|
|
|
+ required Function(String) onSelected,
|
|
|
+ required bool isCustomEnabled,
|
|
|
+ required VoidCallback onCustomClick,
|
|
|
+ required VoidCallback nextClick,
|
|
|
+ required bool isShowEmoji,
|
|
|
+ }) {
|
|
|
+ return Obx(() {
|
|
|
+ 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: 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: Wrap(
|
|
|
+ alignment: WrapAlignment.center,
|
|
|
+ spacing: 8.0.r,
|
|
|
+ runSpacing: 10.r,
|
|
|
+ children: [
|
|
|
+ ...items.map((item) {
|
|
|
+ final emoji = item.emoji ?? "";
|
|
|
+ final name = item.name ?? "";
|
|
|
+ return Obx(() {
|
|
|
+ bool isSelected = selectedLabels.contains(name);
|
|
|
+ return Stack(
|
|
|
+ children: [
|
|
|
+ ChoiceChip(
|
|
|
+ label: Row(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ isShowEmoji ? "$emoji$name" : name,
|
|
|
+ style: TextStyle(
|
|
|
+ color:
|
|
|
+ isSelected
|
|
|
+ ? Color(0xFFF5F4F9)
|
|
|
+ : Color(0xFF755BAB),
|
|
|
+ fontSize: 14.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ showCheckmark: false,
|
|
|
+ selected: isSelected,
|
|
|
+ selectedColor: Color(0xFFB782FF),
|
|
|
+ backgroundColor: Colors.white,
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ side: BorderSide(
|
|
|
+ width: 1.w,
|
|
|
+ color: const Color(0x4C755BAB),
|
|
|
+ ),
|
|
|
+ borderRadius: BorderRadius.circular(31.r),
|
|
|
+ ),
|
|
|
+ onSelected: (selected) {
|
|
|
+ onSelected(name);
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }),
|
|
|
+ Visibility(
|
|
|
+ visible: isCustomEnabled,
|
|
|
+ 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: 86.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 _buildInterestsPage() {
|
|
|
- List<Map<String, String>> interestOptions = [
|
|
|
- {'emoji': '🐱', 'label': '撸猫'},
|
|
|
- {'emoji': '🐶', 'label': '撸狗'},
|
|
|
- {'emoji': '📷', 'label': '拍照摄影'},
|
|
|
- {'emoji': '🔍', 'label': '美食烹饪'},
|
|
|
- {'emoji': '👗', 'label': '潮流穿搭'},
|
|
|
- {'emoji': '🎮', 'label': '竞技游戏'},
|
|
|
- {'emoji': '🎵', 'label': '潮流穿搭潮流穿搭潮流穿搭潮流穿搭'},
|
|
|
- {'emoji': '✈️', 'label': '旅游'},
|
|
|
- ];
|
|
|
|
|
|
- return Column(
|
|
|
- children: [
|
|
|
- Text('你的兴趣爱好是什么?'),
|
|
|
- Align(
|
|
|
- alignment: Alignment.center,
|
|
|
- child: Wrap(
|
|
|
- alignment: WrapAlignment.center,
|
|
|
- spacing: 8.0,
|
|
|
- runSpacing: 8.0,
|
|
|
- children: [
|
|
|
- ...interestOptions.map((item) {
|
|
|
- String emoji = item['emoji']!;
|
|
|
- String label = item['label']!;
|
|
|
- return Obx(() => ChoiceChip(
|
|
|
- label: Row(
|
|
|
- mainAxisSize: MainAxisSize.min,
|
|
|
- children: [
|
|
|
- Text(emoji, style: TextStyle(fontSize: 18)),
|
|
|
- SizedBox(width: 4),
|
|
|
- Text(label),
|
|
|
- ],
|
|
|
- ),
|
|
|
- selected: controller.interests.contains(label),
|
|
|
- selectedColor: Colors.purple.shade100,
|
|
|
- backgroundColor: Colors.white,
|
|
|
- shape: RoundedRectangleBorder(
|
|
|
- side: BorderSide(color: Colors.purple.shade300),
|
|
|
- borderRadius: BorderRadius.circular(20),
|
|
|
- ),
|
|
|
- onSelected: (selected) {
|
|
|
- if (selected && controller.interests.length < 3) {
|
|
|
- controller.interests.add(label);
|
|
|
- } else if (!selected) {
|
|
|
- controller.interests.remove(label);
|
|
|
- }
|
|
|
- },
|
|
|
- ));
|
|
|
- }).toList(),
|
|
|
- GestureDetector(
|
|
|
- onTap: () {
|
|
|
- // TODO: 处理自定义兴趣的逻辑,例如弹出输入框
|
|
|
+ // 选择爱好页面
|
|
|
+ Widget _buildHobbiesPage() {
|
|
|
+ return _buildSelectionPage(
|
|
|
+ title: StringName.characterCustomHobbiesTitle,
|
|
|
+ subtitle:
|
|
|
+ "(最多选择${controller.currentCharacterCustomConfig.value?.maxHobbyNum ?? 3}个)",
|
|
|
+ items: controller.hobbiesLabelsList,
|
|
|
+ selectedLabels: controller.hobbiesSelectLabels,
|
|
|
+ isShowEmoji: true,
|
|
|
+ onSelected: (name) {
|
|
|
+ controller.selectHobby(name);
|
|
|
+ },
|
|
|
+ isCustomEnabled:
|
|
|
+ controller.currentCharacterCustomConfig.value?.customHobby == true,
|
|
|
+ onCustomClick: () {
|
|
|
+ controller.clickHobbiesCustom();
|
|
|
+ },
|
|
|
+ nextClick: () {
|
|
|
+ controller.clickHobbiesNext();
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 选择性格页面
|
|
|
+ Widget _buildCharacterPage() {
|
|
|
+ return _buildSelectionPage(
|
|
|
+ title: StringName.characterCustomcharacterTitle,
|
|
|
+ subtitle:
|
|
|
+ "(最多选择${controller.currentCharacterCustomConfig.value?.maxCharacterNum ?? 3}个)",
|
|
|
+ items: controller.characterLabelsList,
|
|
|
+ selectedLabels: controller.characterSelectLabels,
|
|
|
+ isShowEmoji: false,
|
|
|
+ onSelected: (name) {
|
|
|
+ controller.selectCharacter(name);
|
|
|
+ },
|
|
|
+ isCustomEnabled:
|
|
|
+ controller.currentCharacterCustomConfig.value?.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;
|
|
|
},
|
|
|
- child: Container(
|
|
|
- padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
|
- decoration: BoxDecoration(
|
|
|
- border: Border.all(color: Colors.purple.shade300),
|
|
|
- borderRadius: BorderRadius.circular(20),
|
|
|
- ),
|
|
|
- child: Row(
|
|
|
- mainAxisSize: MainAxisSize.min,
|
|
|
- children: [
|
|
|
- Icon(Icons.add, size: 18, color: Colors.purple.shade300),
|
|
|
- SizedBox(width: 4),
|
|
|
- Text('自定义', style: TextStyle(color: Colors.purple.shade300)),
|
|
|
- ],
|
|
|
+ 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();
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ });
|
|
|
}
|
|
|
}
|