瀏覽代碼

[feat]增加新人流程,修改android app包名,app名字

云天逵 7 月之前
父節點
當前提交
6885d028d9
共有 58 個文件被更改,包括 3205 次插入99 次删除
  1. 1 1
      android/app/src/main/AndroidManifest.xml
  2. 2 2
      android/build.gradle.kts
  3. 1 0
      assets/anim/anim_new_user_data.json
  4. 二進制
      assets/images/bg_new_user_result.webp
  5. 二進制
      assets/images/bg_new_user_result_intimacy.webp
  6. 二進制
      assets/images/icon_custom_character_add_dialog_close.webp
  7. 二進制
      assets/images/icon_custom_character_add_dialog_select.webp
  8. 二進制
      assets/images/icon_custom_character_add_dialog_title.webp
  9. 二進制
      assets/images/icon_keyboard_default_avatar.webp
  10. 二進制
      assets/images/icon_new_user_birthday_logo.webp
  11. 二進制
      assets/images/icon_new_user_open_now.webp
  12. 二進制
      assets/images/icon_new_user_result_love_left.webp
  13. 二進制
      assets/images/icon_new_user_result_love_right.webp
  14. 二進制
      assets/images/icon_new_user_result_open_desc.webp
  15. 二進制
      assets/images/icon_new_user_result_title.webp
  16. 二進制
      assets/images/icon_new_user_stages_ambiguous.webp
  17. 二進制
      assets/images/icon_new_user_zodiac_left.webp
  18. 二進制
      assets/images/icon_new_user_zodiac_right.webp
  19. 32 0
      assets/string/base/string.xml
  20. 10 1
      lib/data/api/atmob_api.dart
  21. 35 1
      lib/data/api/atmob_api.g.dart
  22. 22 0
      lib/data/api/request/keyboard_meme_explain_request.dart
  23. 74 0
      lib/data/api/request/keyboard_meme_explain_request.g.dart
  24. 34 0
      lib/data/api/response/keyboard_meme_explain_response.dart
  25. 25 0
      lib/data/api/response/keyboard_meme_explain_response.g.dart
  26. 11 1
      lib/data/repository/characters_repository.dart
  27. 5 0
      lib/data/repository/config_repository.dart
  28. 19 6
      lib/data/repository/keyboard_repository.dart
  29. 15 0
      lib/di/get_it.config.dart
  30. 240 0
      lib/dialog/custom_character/custom_character_another_add_dialog.dart
  31. 34 0
      lib/dialog/keyboard_generating_dialog.dart
  32. 111 0
      lib/dialog/select_birthday_dialog.dart
  33. 18 14
      lib/module/character_custom/list/character_custom_list_controller.dart
  34. 4 13
      lib/module/character_custom/list/character_custom_list_page.dart
  35. 14 2
      lib/module/mine/mine_controller.dart
  36. 213 0
      lib/module/new_user/new_user_controller.dart
  37. 181 0
      lib/module/new_user/new_user_page.dart
  38. 178 0
      lib/module/new_user/result/new_user_result_controller.dart
  39. 401 0
      lib/module/new_user/result/new_user_result_page.dart
  40. 32 0
      lib/module/new_user/step/birthday/step_birthday_logic.dart
  41. 153 0
      lib/module/new_user/step/birthday/step_birthday_view.dart
  42. 16 0
      lib/module/new_user/step/gender/step_gender_logic.dart
  43. 204 0
      lib/module/new_user/step/gender/step_gender_view.dart
  44. 26 0
      lib/module/new_user/step/intimacy/step_intimacy_stages_logic.dart
  45. 128 0
      lib/module/new_user/step/intimacy/step_intimacy_stages_view.dart
  46. 10 0
      lib/module/new_user/step/nickname/step_nickname_logic.dart
  47. 106 0
      lib/module/new_user/step/nickname/step_nickname_view.dart
  48. 103 0
      lib/module/new_user/step/partner/step_partner_logic.dart
  49. 395 0
      lib/module/new_user/step/partner/step_partner_view.dart
  50. 77 0
      lib/resource/assets.gen.dart
  51. 50 0
      lib/resource/string.gen.dart
  52. 50 35
      lib/router/app_pages.dart
  53. 40 0
      lib/utils/age_zodiac_sign_util.dart
  54. 48 12
      lib/utils/intimacy_util.dart
  55. 8 0
      lib/utils/styles.dart
  56. 22 9
      lib/widget/auto_scroll_list_view.dart
  57. 5 2
      lib/widget/birthday_date_picker.dart
  58. 52 0
      lib/widget/step_progress_bar.dart

+ 1 - 1
android/app/src/main/AndroidManifest.xml

@@ -7,7 +7,7 @@
         android:name=".AtmobApplication"
         android:allowBackup="false"
         android:icon="@mipmap/ic_launcher"
-        android:label="keyboard">
+        android:label="追爱小键盘">
         <activity
             android:name=".MainActivity"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

+ 2 - 2
android/build.gradle.kts

@@ -1,8 +1,8 @@
 allprojects {
     extra.apply {
         set("compileSdkVersion", 35)
-        set("applicationId", "com.jianpan")
-//        set("applicationId", "com.yunshu.chuangyiai")
+//        set("applicationId", "com.jianpan")
+        set("applicationId", "com.qihuan.zhuiai")
         set("minSdkVersion", 23)
         set("targetSdkVersion", 35)
         set("ndkVersion", "27.0.12077973")

文件差異過大導致無法顯示
+ 1 - 0
assets/anim/anim_new_user_data.json


二進制
assets/images/bg_new_user_result.webp


二進制
assets/images/bg_new_user_result_intimacy.webp


二進制
assets/images/icon_custom_character_add_dialog_close.webp


二進制
assets/images/icon_custom_character_add_dialog_select.webp


二進制
assets/images/icon_custom_character_add_dialog_title.webp


二進制
assets/images/icon_keyboard_default_avatar.webp


二進制
assets/images/icon_new_user_birthday_logo.webp


二進制
assets/images/icon_new_user_open_now.webp


二進制
assets/images/icon_new_user_result_love_left.webp


二進制
assets/images/icon_new_user_result_love_right.webp


二進制
assets/images/icon_new_user_result_open_desc.webp


二進制
assets/images/icon_new_user_result_title.webp


二進制
assets/images/icon_new_user_stages_ambiguous.webp


二進制
assets/images/icon_new_user_zodiac_left.webp


二進制
assets/images/icon_new_user_zodiac_right.webp


+ 32 - 0
assets/string/base/string.xml

@@ -330,6 +330,38 @@
     <string name="logout_dialog_confirm">确认</string>
     <string name="logout_dialog_desc">确定退出登录吗?</string>
 
+    <string name="skip">跳过</string>
+    <string name="new_user_gender_title">Hi,欢迎来到追爱小键盘</string>
+    <string name="new_user_gender_desc">您的性别是?</string>
+    <string name="new_user_birthday_title">生日</string>
+    <string name="new_user_birthday_desc">正确的生日,可以匹配到更合适的内容</string>
+    <string name="new_user_nickname_title">填写昵称</string>
+    <string name="new_user_nickname_desc">起个好听的昵称吧!</string>
+    <string name="new_user_intimacy_stages_title">当前阶段</string>
+    <string name="new_user_intimacy_stages_desc">选择适合的阶段,帮你把握回复分寸</string>
+    <string name="new_user_partner_title">完善对方信息</string>
+    <string name="new_user_partner_desc">生成1V1专属恋爱键盘</string>
+
+    <string name="new_user_partner_is">TA是</string>
+    <string name="new_user_partner_later">稍后设置 使用通用模式</string>
+    <string name="new_user_partner_now_generate">立即生成</string>
+    <string name="new_user_partner_birthday">请选择出生年月</string>
+
+
+    <string name="new_user_partner_is_female">TA是女生</string>
+    <string name="new_user_partner_is_male">TA是男生</string>
+    <string name="year">年</string>
+    <string name="month">月</string>
+    <string name="day">日</string>
+    <string name="select_birthday_dialog_title">选择日期</string>
+    <string name="select_birthday_dialog_cancel">取消</string>
+    <string name="select_birthday_dialog_confirm">确定</string>
+    <string name="new_user_result_intimacy_title">亲密度:</string>
+    <string name="new_user_result_intimacy_desc">根据当前的亲密度,建议选择以下人设</string>
+
+
+
+
 
 
 

+ 10 - 1
lib/data/api/atmob_api.dart

@@ -19,6 +19,7 @@ import 'package:keyboard/data/api/request/keyboard_character_update_request.dart
 import 'package:keyboard/data/api/request/keyboard_choose_request.dart';
 import 'package:keyboard/data/api/request/keyboard_generate_request.dart';
 import 'package:keyboard/data/api/request/keyboard_list_request.dart';
+import 'package:keyboard/data/api/request/keyboard_meme_explain_request.dart';
 import 'package:keyboard/data/api/request/keyboard_update_request.dart';
 import 'package:keyboard/data/api/request/login_request.dart';
 import 'package:keyboard/data/api/request/order_pay_request.dart';
@@ -47,6 +48,7 @@ import 'package:keyboard/data/api/response/keyboard_home_info_response.dart'
     show KeyboardHomeInfoResponse;
 import 'package:keyboard/data/api/response/keyboard_list_response.dart';
 import 'package:keyboard/data/api/response/keyboard_love_index_response.dart';
+import 'package:keyboard/data/api/response/keyboard_meme_explain_response.dart';
 import 'package:keyboard/data/api/response/keyboard_prologue_list_response.dart';
 import 'package:keyboard/data/api/response/login_response.dart';
 import 'package:keyboard/data/api/response/new_user_get_character_response.dart';
@@ -95,10 +97,11 @@ abstract class AtmobApi {
 
   //  获取新人流程人设列表
   @POST("/project/keyboard/v1/character/newUser/getCharacter")
-  Future<BaseResponse<NewUserGetCharacterResponse>> getNewUserCharactersPage(
+  Future<BaseResponse<NewUserGetCharacterResponse>> getNewUserCharactersList(
     @Body() AppBaseRequest request,
   );
 
+
   // 获取人设主题
   @POST("/project/keyboard/v1/character/group")
   Future<BaseResponse<CharacterGroupResponse>> getCharactersGroup(
@@ -266,4 +269,10 @@ abstract class AtmobApi {
   Future<BaseResponse<IntimacyAnalyzeResponse>> getIntimacyAnalyze(
     @Body() IntimacyAnalyzeRequest request,
   );
+
+  // 获取星座梗语与解读
+  @POST("/project/keyboard/v1/keyboard/memeExplain")
+  Future<BaseResponse<KeyboardMemeExplainResponse>> getKeyboardMemeExplain(
+    @Body() KeyboardMemeExplainRequest request,
+  );
 }

+ 35 - 1
lib/data/api/atmob_api.g.dart

@@ -212,7 +212,7 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
-  Future<BaseResponse<NewUserGetCharacterResponse>> getNewUserCharactersPage(
+  Future<BaseResponse<NewUserGetCharacterResponse>> getNewUserCharactersList(
     AppBaseRequest request,
   ) async {
     final _extra = <String, dynamic>{};
@@ -1237,6 +1237,40 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<KeyboardMemeExplainResponse>> getKeyboardMemeExplain(
+    KeyboardMemeExplainRequest request,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options = _setStreamType<BaseResponse<KeyboardMemeExplainResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/keyboard/memeExplain',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<KeyboardMemeExplainResponse> _value;
+    try {
+      _value = BaseResponse<KeyboardMemeExplainResponse>.fromJson(
+        _result.data!,
+        (json) =>
+            KeyboardMemeExplainResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
   RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
     if (T != dynamic &&
         !(requestOptions.responseType == ResponseType.bytes ||

+ 22 - 0
lib/data/api/request/keyboard_meme_explain_request.dart

@@ -0,0 +1,22 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'keyboard_meme_explain_request.g.dart';
+
+@JsonSerializable()
+class KeyboardMemeExplainRequest extends AppBaseRequest {
+  @JsonKey(name: "birthday")
+  String birthday;
+
+  @JsonKey(name: "targetBirthday")
+  String targetBirthday;
+
+  KeyboardMemeExplainRequest({
+    required this.birthday,
+    required this.targetBirthday,
+  });
+
+  @override
+  Map<String, dynamic> toJson() => _$KeyboardMemeExplainRequestToJson(this);
+}

+ 74 - 0
lib/data/api/request/keyboard_meme_explain_request.g.dart

@@ -0,0 +1,74 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'keyboard_meme_explain_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+KeyboardMemeExplainRequest _$KeyboardMemeExplainRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    KeyboardMemeExplainRequest(
+        birthday: json['birthday'] as String,
+        targetBirthday: json['targetBirthday'] as String,
+      )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$KeyboardMemeExplainRequestToJson(
+  KeyboardMemeExplainRequest instance,
+) => <String, dynamic>{
+  'appPlatform': instance.appPlatform,
+  'os': instance.os,
+  'osVersion': instance.osVersion,
+  'packageName': instance.packageName,
+  'appVersionName': instance.appVersionName,
+  'appVersionCode': instance.appVersionCode,
+  'channelName': instance.channelName,
+  'appId': instance.appId,
+  'tgPlatform': instance.tgPlatform,
+  'oaid': instance.oaid,
+  'aaid': instance.aaid,
+  'androidId': instance.androidId,
+  'imei': instance.imei,
+  'simImei0': instance.simImei0,
+  'simImei1': instance.simImei1,
+  'mac': instance.mac,
+  'idfa': instance.idfa,
+  'idfv': instance.idfv,
+  'machineId': instance.machineId,
+  'brand': instance.brand,
+  'model': instance.model,
+  'wifiName': instance.wifiName,
+  'region': instance.region,
+  'locLng': instance.locLng,
+  'locLat': instance.locLat,
+  'authToken': instance.authToken,
+  'birthday': instance.birthday,
+  'targetBirthday': instance.targetBirthday,
+};

+ 34 - 0
lib/data/api/response/keyboard_meme_explain_response.dart

@@ -0,0 +1,34 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/keyboard_info.dart';
+
+part 'keyboard_meme_explain_response.g.dart';
+
+@JsonSerializable()
+class KeyboardMemeExplainResponse {
+  // 默契数值
+  @JsonKey(name: "constellation")
+  String? constellation;
+
+  //激情数值
+  @JsonKey(name: "targetConstellation")
+  String? targetConstellation;
+
+  @JsonKey(name: "meme")
+  String? meme;
+
+  @JsonKey(name: "explain")
+  String? explain;
+
+  KeyboardMemeExplainResponse({
+    this.constellation,
+    this.targetConstellation,
+    this.meme,
+    this.explain,
+  });
+
+  factory KeyboardMemeExplainResponse.fromJson(Map<String, dynamic> json) =>
+      _$KeyboardMemeExplainResponseFromJson(json);
+
+  Map<String, dynamic> toJson() => _$KeyboardMemeExplainResponseToJson(this);
+}

+ 25 - 0
lib/data/api/response/keyboard_meme_explain_response.g.dart

@@ -0,0 +1,25 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'keyboard_meme_explain_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+KeyboardMemeExplainResponse _$KeyboardMemeExplainResponseFromJson(
+  Map<String, dynamic> json,
+) => KeyboardMemeExplainResponse(
+  constellation: json['constellation'] as String?,
+  targetConstellation: json['targetConstellation'] as String?,
+  meme: json['meme'] as String?,
+  explain: json['explain'] as String?,
+);
+
+Map<String, dynamic> _$KeyboardMemeExplainResponseToJson(
+  KeyboardMemeExplainResponse instance,
+) => <String, dynamic>{
+  'constellation': instance.constellation,
+  'targetConstellation': instance.targetConstellation,
+  'meme': instance.meme,
+  'explain': instance.explain,
+};

+ 11 - 1
lib/data/repository/characters_repository.dart

@@ -6,6 +6,7 @@ import 'package:keyboard/data/api/response/character_add_response.dart';
 import 'package:keyboard/data/api/response/character_custom_page_response.dart';
 import 'package:keyboard/data/api/response/character_custom_update_response.dart';
 import 'package:keyboard/data/api/response/character_unlock_response.dart';
+import 'package:keyboard/data/api/response/new_user_get_character_response.dart';
 import 'package:keyboard/data/repository/keyboard_repository.dart';
 import '../../base/app_base_request.dart';
 import '../../di/get_it.dart';
@@ -180,10 +181,19 @@ class CharactersRepository {
   // 删除定制人设
   Future<void> deleteCustomCharacter({required String characterId}) {
     return atmobApi
-        .deleteCustomCharacter(CharacterCustomDeleteRequest(characterId: characterId))
+        .deleteCustomCharacter(
+          CharacterCustomDeleteRequest(characterId: characterId),
+        )
         .then(HttpHandler.handle(false));
   }
 
+  // 获取新人人设列表
+  Future<NewUserGetCharacterResponse> getNewUserCharactersList() {
+    return atmobApi
+        .getNewUserCharactersList(AppBaseRequest())
+        .then(HttpHandler.handle(true));
+  }
+
   static CharactersRepository getInstance() =>
       getIt.get<CharactersRepository>();
 }

+ 5 - 0
lib/data/repository/config_repository.dart

@@ -2,6 +2,7 @@ import 'package:injectable/injectable.dart';
 import 'package:keyboard/data/api/response/character_custom_config_response.dart';
 
 import '../../base/app_base_request.dart';
+import '../../di/get_it.dart';
 import '../../utils/async_util.dart';
 import '../../utils/atmob_log.dart';
 import '../../utils/http_handler.dart';
@@ -80,4 +81,8 @@ class ConfigRepository {
         .getCharacterCustomConfig(AppBaseRequest())
         .then(HttpHandler.handle(true));
   }
+
+  static ConfigRepository getInstance() =>
+      getIt.get<ConfigRepository>();
+
 }

+ 19 - 6
lib/data/repository/keyboard_repository.dart

@@ -6,6 +6,7 @@ import 'package:keyboard/data/api/request/keyboard_generate_request.dart';
 import 'package:keyboard/data/api/request/keyboard_list_request.dart';
 import 'package:keyboard/data/api/response/keyboard_generate_response.dart';
 import 'package:keyboard/data/api/response/keyboard_love_index_response.dart';
+import 'package:keyboard/data/api/response/keyboard_meme_explain_response.dart';
 import 'package:keyboard/data/api/response/keyboard_prologue_list_response.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 import 'dart:convert';
@@ -15,6 +16,7 @@ import '../../utils/http_handler.dart';
 import '../api/atmob_api.dart';
 import '../api/request/keyboard_character_list_request.dart';
 import '../api/request/keyboard_choose_request.dart';
+import '../api/request/keyboard_meme_explain_request.dart';
 import '../api/request/keyboard_update_request.dart';
 import '../api/response/keyboard_character_list_response.dart';
 import '../api/response/keyboard_home_info_response.dart';
@@ -51,17 +53,16 @@ class KeyboardRepository {
 
   Future refreshData() async {
     refreshKeyboardList();
-
     await Future.delayed(const Duration(milliseconds: 500));
     // 延迟为了保证首页数据能够正常获取,不然保存的时候,获取太快了,导致还是拉到旧的数值
     refreshUserInfo();
     refreshLoveIndex();
   }
 
-  void refreshUserInfo() {
+  void refreshUserInfo() async {
     homeInfoFuture?.cancel();
     homeInfoFuture = AsyncUtil.retryWithExponentialBackoff(
-          () => getKeyboardHomeInfo(),
+      () => getKeyboardHomeInfo(),
       10,
       predicate: (error) {
         if (error is ServerErrorException) {
@@ -75,7 +76,7 @@ class KeyboardRepository {
   void refreshLoveIndex() {
     homeLoveIndexFuture?.cancel();
     homeLoveIndexFuture = AsyncUtil.retryWithExponentialBackoff(
-          () => getKeyboardLoveIndex(),
+      () => getKeyboardLoveIndex(),
       10,
       predicate: (error) {
         if (error is ServerErrorException) {
@@ -86,8 +87,6 @@ class KeyboardRepository {
     );
   }
 
-
-
   Future cleanData() async {
     _keyboardInfoList.clear();
     getKeyboardHomeInfo();
@@ -216,6 +215,20 @@ class KeyboardRepository {
           return response;
         });
   }
+  Future<KeyboardMemeExplainResponse> getKeyboardMemeExplain({
+    required String birthday,
+    required String targetBirthday,
+  }) {
+    return atmobApi
+        .getKeyboardMemeExplain(
+      KeyboardMemeExplainRequest(
+        birthday: birthday,
+        targetBirthday: targetBirthday,
+      ),
+    )
+        .then(HttpHandler.handle(true));
+  }
+
 
   static KeyboardRepository getInstance() => getIt.get<KeyboardRepository>();
 }

+ 15 - 0
lib/di/get_it.config.dart

@@ -69,6 +69,8 @@ import '../module/keyboard_manage/keyboard_manage_controller.dart' as _i922;
 import '../module/login/login_controller.dart' as _i1008;
 import '../module/main/main_controller.dart' as _i731;
 import '../module/mine/mine_controller.dart' as _i732;
+import '../module/new_user/new_user_controller.dart' as _i701;
+import '../module/new_user/result/new_user_result_controller.dart' as _i576;
 import '../module/profile/edit/profile_edit_controller.dart' as _i344;
 import '../module/profile/profile_controller.dart' as _i244;
 import '../module/store/discount/discount_controller.dart' as _i333;
@@ -222,6 +224,12 @@ extension GetItInjectableX on _i174.GetIt {
     gh.factory<_i161.KeyBoardController>(
       () => _i161.KeyBoardController(gh<_i274.KeyboardRepository>()),
     );
+    gh.factory<_i701.NewUserController>(
+      () => _i701.NewUserController(
+        gh<_i83.AccountRepository>(),
+        gh<_i274.KeyboardRepository>(),
+      ),
+    );
     gh.factory<_i970.CharacterGroupContentController>(
       () => _i970.CharacterGroupContentController(
         gh<_i421.CharactersRepository>(),
@@ -292,6 +300,13 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i495.WechatLoginService>(),
       ),
     );
+    gh.factory<_i576.NewUserResultController>(
+      () => _i576.NewUserResultController(
+        gh<_i83.AccountRepository>(),
+        gh<_i274.KeyboardRepository>(),
+        gh<_i421.CharactersRepository>(),
+      ),
+    );
     gh.factory<_i1059.CharacterCustomListController>(
       () => _i1059.CharacterCustomListController(
         gh<_i421.CharactersRepository>(),

+ 240 - 0
lib/dialog/custom_character/custom_character_another_add_dialog.dart

@@ -0,0 +1,240 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get_rx/src/rx_types/rx_types.dart';
+import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
+import 'package:keyboard/data/bean/keyboard_info.dart';
+import 'package:keyboard/data/repository/characters_repository.dart';
+import 'package:keyboard/data/repository/keyboard_repository.dart';
+import 'package:keyboard/utils/toast_util.dart';
+
+import '../../resource/assets.gen.dart';
+import '../../resource/string.gen.dart';
+import '../../utils/http_handler.dart';
+import '../../utils/styles.dart';
+
+class CustomCharacterAnotherAddDialog {
+  static const tag = "CustomCharacterAnotherAddDialog";
+  static RxInt selectedIndex = RxInt(0);
+  static Rx<KeyboardInfo> selectKeyboard = KeyboardInfo().obs;
+
+  static show({required String customCharacterId}) {
+    SmartDialog.show(
+      backType: SmartBackType.block,
+      clickMaskDismiss: true,
+      alignment: Alignment.bottomCenter,
+      animationType: SmartAnimationType.centerScale_otherSlide,
+      tag: tag,
+      keepSingle: true,
+      builder:
+          (_) => Container(
+            height: 472.h,
+            decoration: ShapeDecoration(
+              color: Color(0xFFF6F5FA),
+              shape: RoundedRectangleBorder(
+                borderRadius: BorderRadius.only(
+                  topLeft: Radius.circular(18.r),
+                  topRight: Radius.circular(18.r),
+                ),
+              ),
+            ),
+            child: Column(
+              children: [
+                Container(
+                  padding: EdgeInsets.only(
+                    left: 16.w,
+                    right: 16.w,
+                    top: 22.w,
+                    bottom: 20.w,
+                  ),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      Assets.images.iconCustomCharacterAddDialogTitle.image(
+                        width: 85.w,
+                        height: 25.w,
+                        fit: BoxFit.contain,
+                      ),
+                      GestureDetector(
+                        onTap: () {
+                          SmartDialog.dismiss(tag: tag);
+                        },
+                        child: Assets.images.iconCustomCharacterAddDialogClose
+                            .image(
+                              width: 24.w,
+                              height: 24.w,
+                              fit: BoxFit.contain,
+                            ),
+                      ),
+                    ],
+                  ),
+                ),
+                Expanded(
+                  child: Stack(
+                    children: [
+                      ListView.separated(
+                        padding: EdgeInsets.only(bottom: 100.h),
+                        itemCount:
+                            KeyboardRepository.getInstance()
+                                .keyboardInfoList
+                                .length,
+                        itemBuilder: (context, index) {
+                          return _buildListItem(
+                            keyboardInfo:
+                                KeyboardRepository.getInstance()
+                                    .keyboardInfoList[index],
+                            index: index,
+                          );
+                        },
+                        separatorBuilder: (BuildContext context, int index) {
+                          if (index ==
+                              KeyboardRepository.getInstance()
+                                      .keyboardInfoList
+                                      .length -
+                                  1) {
+                            return SizedBox(
+                              width: double.infinity,
+                              height: 50.h, // 最后一个元素后面加50空白
+                            );
+                          } else {
+                            return SizedBox(
+                              width: double.infinity,
+                              height: 10.h,
+                            );
+                          }
+                        },
+                      ),
+                      Positioned(
+                        bottom: 20.h,
+                        left: 16.w,
+                        right: 16.w,
+                        child: InkWell(
+                          onTap: () async {
+                            if (selectKeyboard.value.id == null) {
+                              return ToastUtil.show("请选择键盘");
+                            }
+                            try {
+                              await CharactersRepository.getInstance()
+                                  .addCustomCharacter(
+                                    characterId: customCharacterId,
+                                    keyboardId: selectKeyboard.value.id!,
+                                  );
+                              ToastUtil.show("添加成功");
+                              SmartDialog.dismiss(tag: tag);
+                            } catch (error) {
+                              if (error is ServerErrorException) {
+                                ToastUtil.show(error.message);
+                              }
+                            }
+                          },
+                          child: Container(
+                            height: 48.h,
+                            alignment: Alignment.center,
+                            decoration: Styles.getActivateButtonDecoration(
+                              31.r,
+                            ),
+                            child: Text(
+                              StringName.profileSave,
+                              style: Styles.getTextStyleWhiteW500(16.sp),
+                            ),
+                          ),
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+    );
+  }
+
+  static Widget _buildListItem({
+    required KeyboardInfo keyboardInfo,
+    required int index,
+  }) {
+    return GestureDetector(
+      onTap: () {
+        selectKeyboard.value = keyboardInfo;
+        selectedIndex.value = index;
+      },
+      child: Obx(() {
+        return Container(
+          margin: EdgeInsets.only(left: 16.w, right: 16.w),
+          decoration: ShapeDecoration(
+            color: Colors.white,
+            shape: RoundedRectangleBorder(
+              // 只有选中的项目才显示紫色边框
+              side: BorderSide(
+                width: selectedIndex.value == index ? 2 : 0,
+                color:
+                    selectedIndex.value == index
+                        ? const Color(0xFF7D46FC)
+                        : Colors.transparent,
+              ),
+              borderRadius: BorderRadius.circular(12.r),
+            ),
+          ),
+          height: 88.h,
+          padding: EdgeInsets.symmetric(horizontal: 16.w),
+          child: Row(
+            children: [
+              _buildAvatar(imageUrl: keyboardInfo.imageUrl),
+              SizedBox(width: 8.w),
+              _buildKeyboardInfo(keyboardInfo),
+              selectedIndex.value == index?Assets.images.iconCustomCharacterAddDialogSelect.image(width: 26.w,height: 26.w,fit: BoxFit.contain):SizedBox()
+            ],
+          ),
+        );
+      }),
+    );
+  }
+
+  /// 角色头像
+  static Widget _buildAvatar({required String? imageUrl}) {
+    return Container(
+      width: 60.r,
+      height: 60.r,
+      decoration: BoxDecoration(
+        borderRadius: BorderRadius.circular(8),
+        gradient: LinearGradient(
+          begin: Alignment.topCenter,
+          end: Alignment.bottomCenter,
+          colors: [Color(0xffebe6ff), Color(0xffffe6fe)],
+        ),
+      ),
+      child: CachedNetworkImage(
+        imageUrl: imageUrl ?? "",
+        width: 60.r,
+        height: 60.r,
+        fit: BoxFit.cover,
+
+      ),
+    );
+  }
+
+  static Widget _buildKeyboardInfo(KeyboardInfo keyboardInfo) {
+    return Expanded(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              Text(
+                keyboardInfo.name ?? "",
+                style: TextStyle(
+                  color: Colors.black.withAlpha(204),
+                  fontSize: 15.sp,
+                  fontWeight: FontWeight.w500,
+                ),
+              ),
+              SizedBox(width: 4.w),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 34 - 0
lib/dialog/keyboard_generating_dialog.dart

@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+
+class KeyboardGeneratingDialog{
+  static const tag = "KeyboardGeneratingDialog";
+
+  static void show(){
+    SmartDialog.show(tag:tag,
+        backType: SmartBackType.block,
+        clickMaskDismiss: false,
+        alignment: Alignment.center,
+        animationType: SmartAnimationType.centerScale_otherSlide,
+        builder: (_) {
+          return Container(
+            width: 298.w,
+            height: 234.h,
+            decoration: ShapeDecoration(
+              gradient: LinearGradient(
+                begin: Alignment(0.50, 0.00),
+                end: Alignment(0.50, 1.00),
+                colors: [const Color(0xFFE0D5FD), Colors.white],
+              ),
+              shape: RoundedRectangleBorder(
+                borderRadius: BorderRadius.circular(20),
+              ),
+            ),
+          );
+        });
+  }
+  static void hide(){
+    SmartDialog.dismiss(tag: tag);
+  }
+}

+ 111 - 0
lib/dialog/select_birthday_dialog.dart

@@ -0,0 +1,111 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:keyboard/resource/string.gen.dart';
+import 'package:keyboard/utils/styles.dart';
+
+import '../widget/birthday_date_picker.dart';
+
+class SelectBirthdayDialog {
+  static const tag = "SelectBirthdayDialog";
+
+  static show({
+    required DateTime initialDate,
+    required DateTime minimumDate,
+    required Function(DateTime) onDateChanged,
+  }) {
+    DateTime currentDate = initialDate;
+    SmartDialog.show(
+      backType: SmartBackType.block,
+      clickMaskDismiss: true,
+      alignment: Alignment.bottomCenter,
+      animationType: SmartAnimationType.centerScale_otherSlide,
+      tag: tag,
+      keepSingle: true,
+      builder:
+          (_) =>
+          Container(
+            decoration: ShapeDecoration(
+              color: Colors.white,
+              shape: RoundedRectangleBorder(
+                borderRadius: BorderRadius.only(
+                  topLeft: Radius.circular(18.r),
+                  topRight: Radius.circular(18.r),
+                ),
+              ),
+            ),
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: [
+                Container(
+                  padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 16.w),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      GestureDetector(
+                        onTap: () {
+                          SmartDialog.dismiss(tag: tag);
+                        },
+                        child: Container(
+                          width: 87.w,
+                          height: 38.h,
+                          decoration: ShapeDecoration(
+                            color: const Color(0xFFF5F4F9),
+                            shape: RoundedRectangleBorder(
+                              borderRadius: BorderRadius.circular(28.r),
+                            ),
+                          ),
+                          child: Center(
+                            child: Text(
+                              StringName.selectBirthdayDialogCancel,
+                              style: Styles.getTextStyleBlack204W500(14.sp),
+                            ),
+                          ),
+                        ),
+                      ),
+                      Text(
+                        StringName.selectBirthdayDialogTitle,
+                        style: Styles.getTextStyleBlack204W500(16.sp),
+                      ),
+                      GestureDetector(
+                        onTap: () {
+                          onDateChanged(currentDate);
+                          SmartDialog.dismiss(tag: tag);
+                        },
+                        child: Container(
+                          width: 87.w,
+                          height: 38.h,
+                          decoration: Styles.getActivateButtonDecoration(28.r),
+                          child: Center(
+                            child: Text(
+                              StringName.selectBirthdayDialogConfirm,
+                              style: Styles.getTextStyleWhiteW500(14.sp),
+                            ),
+                          ),
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+                SizedBox(height: 30.h),
+                SizedBox(
+                  height: 150.h,
+                  width: 320.w,
+                  child: BirthdayDatePicker(
+                    initialDate: initialDate,
+                    minimumDate: minimumDate,
+                    backgroundColor: Color(0xffF6F5FA),
+                    maximumDate: DateTime.now(),
+                    onDateChanged: (dateTime) {
+                      currentDate = dateTime;
+                    },
+                  ),
+                ),
+                SizedBox(height: 30.h),
+              ],
+            ),
+          ),
+    );
+  }
+}

+ 18 - 14
lib/module/character_custom/list/character_custom_list_controller.dart

@@ -11,6 +11,7 @@ import '../../../data/bean/character_info.dart';
 import '../../../data/bean/keyboard_info.dart';
 import '../../../data/consts/error_code.dart';
 import '../../../dialog/character_details_dialog.dart';
+import '../../../dialog/custom_character/custom_character_another_add_dialog.dart';
 import '../../../resource/string.gen.dart';
 import '../../../utils/atmob_log.dart';
 import '../../../utils/http_handler.dart';
@@ -94,20 +95,23 @@ class CharacterCustomListController extends BaseController {
 
   void itemAddButtonClick(CharacterInfo characterInfo) async {
     AtmobLog.d(tag, 'characterInfo ${characterInfo.toJson()} ');
-    try {
-      if (characterInfo.id != null) {
-        await charactersRepository.addCustomCharacter(
-          characterId: characterInfo.id!,
-          keyboardId: _currentKeyboardInfo.value.id.toString(),
-        );
-      }
-      ToastUtil.show("使用成功");
-      Get.back(result: characterInfo);
-    } catch (error) {
-      if (error is ServerErrorException) {
-        ToastUtil.show(error.message);
-      }
-    }
+    CustomCharacterAnotherAddDialog.show(customCharacterId:characterInfo.id!);
+
+
+    // try {
+    //   if (characterInfo.id != null) {
+    //     await charactersRepository.addCustomCharacter(
+    //       characterId: characterInfo.id!,
+    //       keyboardId: _currentKeyboardInfo.value.id.toString(),
+    //     );
+    //   }
+    //   ToastUtil.show("使用成功");
+    //   Get.back(result: characterInfo);
+    // } catch (error) {
+    //   if (error is ServerErrorException) {
+    //     ToastUtil.show(error.message);
+    //   }
+    // }
   }
 
   void itemEditClick(CharacterInfo characterInfo) async {

+ 4 - 13
lib/module/character_custom/list/character_custom_list_page.dart

@@ -264,14 +264,12 @@ class CharacterCustomListPage extends BasePage<CharacterCustomListController> {
         decoration: BoxDecoration(
           borderRadius: BorderRadius.circular(50.r),
           gradient:
-              characterInfo.isAdd == true
-                  ? null
-                  : const LinearGradient(
+              const LinearGradient(
                     colors: [Color(0xFF7D46FC), Color(0xFFBC87FF)],
                     begin: Alignment.topLeft,
                     end: Alignment.bottomRight,
                   ),
-          color: characterInfo.isAdd == true ? const Color(0xFFEDE8FF) : null,
+
         ),
         child: Row(
           mainAxisAlignment: MainAxisAlignment.center,
@@ -285,16 +283,9 @@ class CharacterCustomListPage extends BasePage<CharacterCustomListController> {
                 ), // 锁定图标
               ),
             Text(
-              characterInfo.isLock == true && characterInfo.isVip == true
-                  ? StringName.characterUnlock
-                  : characterInfo.isAdd == true
-                  ? StringName.characterAdded
-                  : StringName.characterAdd,
+              StringName.characterAdd,
               style: TextStyle(
-                color:
-                    characterInfo.isAdd == true
-                        ? const Color(0xFF7D46FC)
-                        : Colors.white,
+                color: Colors.white,
                 fontSize: 14.sp,
                 fontWeight: FontWeight.w500,
               ),

+ 14 - 2
lib/module/mine/mine_controller.dart

@@ -7,10 +7,12 @@ import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/module/about/about_page.dart';
 import 'package:keyboard/module/feedback/feedback_page.dart';
+import 'package:keyboard/module/new_user/new_user_page.dart';
 import 'package:keyboard/module/store/discount/discount_view.dart';
 import 'package:keyboard/module/user_info/user_info_page.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 
+import '../../data/bean/keyboard_info.dart';
 import '../../data/consts/build_config.dart';
 import '../../data/consts/error_code.dart';
 import '../../data/repository/account_repository.dart';
@@ -20,6 +22,7 @@ import '../../plugins/keyboard_method_handler.dart';
 import '../../resource/colors.gen.dart';
 import '../intimacy_analyse/intimacy_analyse_page.dart';
 import '../keyboard_guide/keyboard_guide_page.dart';
+import '../new_user/result/new_user_result_page.dart';
 import '../profile/profile_page.dart';
 import '../store/discount/discount_controller.dart';
 import '../store/suprise/surprise_dialog.dart';
@@ -75,12 +78,21 @@ class MineController extends BaseController {
 
   clickTutorials() {
     debugPrint('clickTutorials');
-
+    NewUserPage.start();
   }
 
 
   longClickTutorials() {
-
+    NewUserResultPage.start(
+      newUserKeyboardInfo:  KeyboardInfo(
+        id: "w_1006",
+        name: "王金啊",
+        gender: 2,
+        imageUrl: "http://cdn.atmob.com/keyboard/avatar/default.png",
+        intimacy: 30,
+        birthday: "2021-11-10",
+      )
+    );
   }
 
   clickPersonalProfile() {

+ 213 - 0
lib/module/new_user/new_user_controller.dart

@@ -0,0 +1,213 @@
+import 'package:flutter/material.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/data/bean/keyboard_info.dart';
+import 'package:keyboard/data/repository/keyboard_repository.dart';
+import 'package:keyboard/main.dart';
+import 'package:keyboard/module/main/main_page.dart';
+import 'package:keyboard/module/new_user/result/new_user_result_page.dart';
+import 'package:keyboard/module/new_user/step/birthday/step_birthday_logic.dart';
+import 'package:keyboard/module/new_user/step/gender/step_gender_logic.dart';
+import 'package:keyboard/module/new_user/step/intimacy/step_intimacy_stages_logic.dart';
+import 'package:keyboard/module/new_user/step/nickname/step_nickname_logic.dart';
+import 'package:keyboard/module/new_user/step/partner/step_partner_logic.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+import 'package:intl/intl.dart';
+import '../../base/base_controller.dart';
+import '../../data/api/response/keyboard_generate_response.dart';
+import '../../data/bean/default_avatar_info.dart';
+import '../../data/repository/account_repository.dart';
+import 'package:get/get.dart';
+
+import '../../dialog/keyboard_generating_dialog.dart';
+import '../../utils/error_handler.dart';
+import '../../utils/http_handler.dart';
+import '../../utils/toast_util.dart';
+
+enum NewUserStepType {
+  stepGenDer(0),
+  stepBirthday(1),
+  stepNickname(2),
+  stepIntimacyStages(3),
+  stepPartner(4);
+
+  final int value;
+
+  const NewUserStepType(this.value);
+}
+
+@injectable
+class NewUserController extends BaseController
+    with
+        StepGenDerLogic,
+        StepBirthdayLogic,
+        StepNicknameLogic,
+        StepIntimacyStagesLogic,
+        StepPartnerLogic {
+  final AccountRepository accountRepository;
+  final KeyboardRepository keyboardRepository;
+
+  NewUserController(this.accountRepository, this.keyboardRepository);
+
+  final RxInt _currentStep = 1.obs;
+
+  int get currentStep => _currentStep.value;
+
+  final RxInt _totalSteps = 5.obs;
+
+  int get totalSteps => _totalSteps.value;
+
+  final PageController pageController = PageController();
+
+  final Rx<KeyboardInfo> _keyboardInfo = KeyboardInfo().obs;
+
+  @override
+  void onInit() {
+    super.onInit();
+
+    updateAvatarListsAndSelectFirst(currentDefaultAvatarInfo.value);
+
+    ever<DefaultAvatarInfo?>(currentDefaultAvatarInfo, (info) {
+      updateAvatarListsAndSelectFirst(info);
+    });
+  }
+
+  void clickBack() {
+    if (currentStep > 1) {
+      pageController.previousPage(
+        duration: const Duration(milliseconds: 300),
+        curve: Curves.easeOut,
+      );
+      _currentStep.value--;
+    } else {
+      Get.back();
+    }
+  }
+
+  void clickNextButton() {
+    if (!isCurrentStepValid) {
+      ToastUtil.show(currentStepValidationError ?? "请完成当前步骤");
+      return;
+    }
+
+    if (currentStep == totalSteps) {
+      _generateKeyboard();
+    } else {
+      pageController.nextPage(
+        duration: const Duration(milliseconds: 300),
+        curve: Curves.easeIn,
+      );
+      _settingUseInfo();
+
+      _currentStep.value++;
+    }
+  }
+
+  void clickUseGeneralMode() {
+    MainPage.start();
+  }
+
+  void clickSkip() {
+    MainPage.start();
+  }
+
+  /// 当前步骤是否完成验证
+  bool get isCurrentStepValid {
+    switch (currentStep) {
+      case 1: // 性别
+        return currentGender != null;
+      case 2: // 生日
+        return currentBirthday != null;
+      case 3: // 昵称
+        return nickname.trim().isNotEmpty;
+      case 4: // 亲密阶段
+        return currentIntimacyMedian > 0;
+      case 5: // 伴侣信息
+        return partnerName.value.trim().isNotEmpty &&
+            currentPartnerBirthday != null &&
+            partnerAvatarUrl.isNotEmpty &&
+            partnerGender != null;
+      default:
+        return false;
+    }
+  }
+
+  /// 获取当前步骤未完成提示
+  String? get currentStepValidationError {
+    switch (currentStep) {
+      case 1:
+        return currentGender == null ? "请选择性别" : null;
+      case 2:
+        return currentBirthday == null ? "请选择生日" : null;
+      case 3:
+        if (nickname.trim().isEmpty) return "请输入昵称";
+        if (nickname.contains(' ')) return "昵称不能包含空格";
+        return null;
+      case 4:
+        return currentIntimacyMedian == 0 ? "请选择亲密阶段" : null;
+      case 5:
+        if (partnerName.value.isEmpty) return "请填写TA姓名";
+        if (partnerGender == null) return "请选择TA性别";
+        if (currentPartnerBirthday == null) return "请填写TA生日";
+        if (partnerAvatarUrl.isEmpty) return "请选择TA头像";
+        return null;
+      default:
+        return null;
+    }
+  }
+
+  Future<void> _settingUseInfo() async {
+    try {
+      accountRepository.setUserInfo(
+        name: nickname.value,
+        birthday:
+            currentBirthday != null
+                ? DateFormat('yyyy-MM-dd').format(currentBirthday!)
+                : null,
+        gender: currentGender,
+        imageUrl: null,
+      );
+    } catch (error) {
+      if (error is ServerErrorException) {
+        ToastUtil.show(error.message);
+      } else {
+        AtmobLog.i(tag, error.toString());
+      }
+    }
+  }
+
+  Future<void> _generateKeyboard() async {
+    KeyboardGeneratingDialog.show();
+    try {
+      KeyboardGenerateResponse keyboardGenerateResponse =
+          await keyboardRepository.getKeyboardGenerate(
+            name: partnerName.value,
+            gender: partnerGender!,
+            imageUrl: partnerAvatarUrl,
+            birthday: DateFormat('yyyy-MM-dd').format(currentPartnerBirthday!),
+            intimacy: currentIntimacyMedian,
+          );
+      if (keyboardGenerateResponse.keyboardInfo == null) {
+        ToastUtil.show("生成失败");
+        KeyboardGeneratingDialog.hide();
+        return;
+      }
+      _keyboardInfo.value = keyboardGenerateResponse.keyboardInfo!;
+
+      keyboardRepository.refreshData();
+      Future.delayed(const Duration(seconds: 3), () {
+        KeyboardGeneratingDialog.hide();
+        NewUserResultPage.start(
+          newUserKeyboardInfo: _keyboardInfo.value,
+        );
+      });
+    } catch (error) {
+      KeyboardGeneratingDialog.hide();
+      if (error is ServerErrorException) {
+        ToastUtil.show(error.message);
+        AtmobLog.d(tag, '_generateKeyboard error: ${error.message}');
+      } else {
+        AtmobLog.d(tag, '_generateKeyboard error: $error');
+      }
+    }
+  }
+}

+ 181 - 0
lib/module/new_user/new_user_page.dart

@@ -0,0 +1,181 @@
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/module/new_user/new_user_controller.dart';
+import 'package:flutter/material.dart';
+import 'package:keyboard/module/new_user/step/birthday/step_birthday_view.dart';
+import 'package:keyboard/module/new_user/step/gender/step_gender_view.dart';
+import 'package:keyboard/module/new_user/step/intimacy/step_intimacy_stages_view.dart';
+import 'package:keyboard/module/new_user/step/nickname/step_nickname_view.dart';
+import 'package:keyboard/module/new_user/step/partner/step_partner_view.dart';
+import 'package:keyboard/router/app_pages.dart';
+
+import '../../resource/assets.gen.dart';
+import '../../resource/string.gen.dart';
+import '../../utils/styles.dart';
+import '../../widget/step_progress_bar.dart';
+
+class NewUserPage extends BasePage<NewUserController> {
+  const NewUserPage({super.key});
+
+  static start() {
+    Get.toNamed(RoutePath.newUser);
+  }
+
+  @override
+  Color backgroundColor() {
+    return const Color(0xFFF6F5FA);
+  }
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        IgnorePointer(child: Assets.images.bgMine.image(width: 360.w)),
+        SafeArea(
+          child: Stack(
+            children: [
+              Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  _buildTitle(),
+                  Container(
+                    margin: EdgeInsets.only(top: 22.w, left: 32.w, right: 32.w),
+                    child: Obx(() {
+                      return StepProgressBar(
+                        currentStep: controller.currentStep,
+                        totalSteps: controller.totalSteps,
+                      );
+                    }),
+                  ),
+                  SizedBox(height: 17.w),
+                  Expanded(
+                    child: PageView(
+                      controller: controller.pageController,
+                      physics: const NeverScrollableScrollPhysics(),
+                      children: const [
+                        StepGenderView(),
+                        StepBirthdayView(),
+                        StepNicknameView(),
+                        StepIntimacyStagesView(),
+                        StepPartnerView(),
+                      ],
+                    ),
+                  ),
+                ],
+              ),
+              Align(
+                alignment: Alignment.bottomCenter,
+                child: Container(
+                  margin: EdgeInsets.only(
+                    bottom: 50.w,
+                    left: 50.w,
+                    right: 50.w,
+                  ),
+                  child: Obx(() {
+                    return _buildNextButton(
+                      onTap: () {
+                        controller.clickNextButton();
+                      },
+                      isEnable: controller.isCurrentStepValid,
+                    );
+                  }),
+                ),
+              ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+
+  _buildTitle() {
+    return Container(
+      alignment: Alignment.centerLeft,
+      padding: EdgeInsets.only(top: 12.h, left: 16.w, right: 16.w),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: [
+          GestureDetector(
+            onTap: () => controller.clickBack(),
+            child: Assets.images.iconMineBackArrow.image(
+              width: 24.w,
+              height: 24.w,
+            ),
+          ),
+
+          GestureDetector(
+            onTap: () => controller.clickSkip(),
+            child: Text(
+              StringName.skip,
+              style: Styles.getTextStyleBlack128W400(14.sp),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildNextButton({required VoidCallback onTap, required isEnable}) {
+    return GestureDetector(
+      onTap: () {
+        onTap();
+      },
+      child: Row(
+        children: [
+          Column(
+            mainAxisSize: MainAxisSize.min,
+            children: [
+              Container(
+                width: 260.w,
+                height: 48.h,
+                decoration:
+                    isEnable
+                        ? Styles.getActivateButtonDecoration(50.r)
+                        : Styles.getInactiveButtonDecoration(50.r),
+                child: Center(
+                  child: Text(
+                    controller.currentStep == controller.totalSteps
+                        ? StringName.newUserPartnerNowGenerate
+                        : StringName.nextStep,
+                    style: TextStyle(
+                      color: Colors.white,
+                      fontSize: 16.sp,
+                      fontWeight: FontWeight.w500,
+                    ),
+                  ),
+                ),
+              ),
+              Visibility(
+                visible: controller.currentStep == controller.totalSteps,
+                child: SizedBox(height: 25.w),
+              ),
+              Visibility(
+                visible: controller.currentStep == controller.totalSteps,
+                child: GestureDetector(
+                  onTap: controller.clickUseGeneralMode,
+
+                  child: Text(
+                    StringName.newUserPartnerLater,
+                    style: TextStyle(
+                      color: Colors.black.withAlpha(87),
+                      fontSize: 14.sp,
+
+                    ),
+                  ),
+                ),
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 178 - 0
lib/module/new_user/result/new_user_result_controller.dart

@@ -0,0 +1,178 @@
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/data/bean/character_info.dart';
+import 'package:keyboard/data/repository/account_repository.dart';
+import 'package:keyboard/data/repository/characters_repository.dart';
+import 'package:keyboard/data/repository/keyboard_repository.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+
+import '../../../data/api/response/keyboard_meme_explain_response.dart';
+import '../../../data/api/response/user_info_response.dart';
+import '../../../data/bean/keyboard_info.dart';
+import '../../../resource/string.gen.dart';
+import '../../../utils/age_zodiac_sign_util.dart';
+import '../../../utils/error_handler.dart';
+import '../../../utils/http_handler.dart';
+import '../../../utils/toast_util.dart';
+
+@injectable
+class NewUserResultController extends BaseController {
+  final tag = "NewUserResultController";
+
+  final AccountRepository accountRepository;
+
+  final KeyboardRepository keyboardRepository;
+
+  final CharactersRepository charactersRepository;
+
+  Rxn<UserInfoResponse> get _userInfo => accountRepository.userInfo;
+
+  UserInfoResponse? get userInfo => _userInfo.value;
+
+  Rx<KeyboardMemeExplainResponse> keyboardMemeExplain =
+      KeyboardMemeExplainResponse().obs;
+
+  final RxList<CharacterInfo> charactersList = <CharacterInfo>[].obs;
+
+  final RxList<CharacterInfo> selectCharactersList = <CharacterInfo>[].obs;
+
+  final Rx<KeyboardInfo> _newUserKeyboardInfo =
+      KeyboardInfo(
+        id: "w_1006",
+        name: "王金啊",
+        gender: 2,
+        imageUrl: "http://cdn.atmob.com/keyboard/avatar/default.png",
+        intimacy: 30,
+        birthday: "2021-11-10",
+      ).obs;
+
+  KeyboardInfo get newUserKeyboardInfo => _newUserKeyboardInfo.value;
+
+  NewUserResultController(
+    this.accountRepository,
+    this.keyboardRepository,
+    this.charactersRepository,
+  );
+
+  @override
+  void onInit() async {
+    super.onInit();
+    try {
+      _newUserKeyboardInfo.value =
+          Get.arguments["newUserKeyboardInfo"] as KeyboardInfo;
+    } catch (e) {
+      AtmobLog.e(tag, "获取新用户键盘信息失败: $e");
+    }
+    getMemeExplain();
+    ever(_userInfo, (userInfo) {
+      if (userInfo != null) {
+        AtmobLog.d(tag, "获取用户信息成功");
+        getMemeExplain();
+      } else {
+        AtmobLog.e(tag, "获取用户信息失败");
+      }
+    });
+
+    getNewUserCharacterList();
+  }
+
+  clickBack() {
+    Get.back();
+  }
+
+  void clickOpenNow() async {
+    AtmobLog.d(tag, "点击立即使用");
+    AtmobLog.i(tag, 'clickSave keyboardChanged');
+    if (_newUserKeyboardInfo.value.id == null ||
+        _newUserKeyboardInfo.value.id!.isEmpty) {
+      return;
+    }
+    List<String> characterIds =
+        selectCharactersList.map((e) => e.id).cast<String>().toList();
+    keyboardRepository
+        .keyboardCharacterUpdate(
+          keyboardId: _newUserKeyboardInfo.value.id!,
+          characterIds: characterIds,
+        )
+        .then((_) {
+          print(
+            '$tag keyboardCharacterUpdate success ${characterIds.toList()}',
+          );
+          AtmobLog.d(tag, "更新键盘人设成功");
+          ToastUtil.show(StringName.keyboardSaveSuccess);
+        })
+        .catchError((error) {
+          if (error is ServerErrorException) {
+            ToastUtil.show(error.message);
+           }
+      
+        });
+  }
+
+  Future<void> getMemeExplain() async {
+    try {
+      if (!_isValid(userInfo?.birthday) ||
+          !_isValid(_newUserKeyboardInfo.value.birthday)) {
+        AtmobLog.e(tag, "生日信息不完整,无法获取星座梗语与解读");
+        return;
+      }
+      final result = await keyboardRepository.getKeyboardMemeExplain(
+        birthday: userInfo!.birthday!,
+        targetBirthday: _newUserKeyboardInfo.value.birthday!,
+      );
+      keyboardMemeExplain.value = result;
+      AtmobLog.d(tag, "获取星座梗语与解读成功");
+    } catch (e) {
+      AtmobLog.e(tag, "获取星座梗语与解读失败: $e");
+    }
+  }
+
+  Future<void> getNewUserCharacterList() async {
+    try {
+      final result = await charactersRepository.getNewUserCharactersList();
+      charactersList.value = result.characterInfo;
+    } catch (e) {
+      AtmobLog.e(tag, "获取新用户角色列表失败: $e");
+    }
+  }
+
+  void onSelected(CharacterInfo characterInfo) {
+    if (selectCharactersList.contains(characterInfo)) {
+      selectCharactersList.remove(characterInfo);
+    } else {
+      selectCharactersList.add(characterInfo);
+    }
+  }
+
+  bool _isValid(String? s) => s != null && s.trim().isNotEmpty;
+
+  Zodiac? get zodiacLabelFromNewUserKeyboardInfo {
+    final birthday = newUserKeyboardInfo.birthday;
+    final gender = newUserKeyboardInfo.gender;
+    if (birthday?.isEmpty != false || gender == null) return null;
+
+    final zodiac = AgeZodiacSignUtil.getZodiacWithGenderLabel(
+      birthday!,
+      gender,
+    );
+    if (zodiac.name == '未知星座') return null;
+
+    return zodiac;
+  }
+
+  Zodiac? get zodiacLabelFromUserInfo {
+    final birthday = userInfo?.birthday;
+    final gender = userInfo?.gender;
+
+    if (birthday?.isEmpty != false || gender == null) return null;
+
+    final zodiac = AgeZodiacSignUtil.getZodiacWithGenderLabel(
+      birthday!,
+      gender,
+    );
+    if (zodiac.name == '未知星座') return null;
+
+    return zodiac;
+  }
+}

+ 401 - 0
lib/module/new_user/result/new_user_result_page.dart

@@ -0,0 +1,401 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/data/bean/keyboard_info.dart';
+import 'package:keyboard/resource/string.gen.dart';
+import 'package:keyboard/utils/age_zodiac_sign_util.dart';
+import 'package:lottie/lottie.dart';
+import '../../../data/bean/character_info.dart';
+import '../../../resource/assets.gen.dart';
+import '../../../router/app_pages.dart';
+import '../../../widget/auto_scroll_list_view.dart';
+import '../../../widget/avatar/avatar_image_widget.dart';
+import 'new_user_result_controller.dart';
+
+class NewUserResultPage extends BasePage<NewUserResultController> {
+  const NewUserResultPage({super.key});
+
+  static start({required KeyboardInfo newUserKeyboardInfo}) {
+    Get.toNamed(
+      RoutePath.newUserResult,
+      arguments: {"newUserKeyboardInfo": newUserKeyboardInfo},
+    );
+  }
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  bool statusBarDarkFont() {
+    return false;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        Assets.images.bgNewUserResult.image(
+          width: double.infinity,
+          fit: BoxFit.fill,
+        ),
+        SafeArea(
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Padding(
+                padding: EdgeInsets.only(left: 16.w, top: 12.w),
+                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: [
+                      SizedBox(height: 30.w),
+                      Center(
+                        child: Assets.images.iconNewUserResultTitle.image(
+                          width: 246.w,
+                          height: 41.2.w,
+                          fit: BoxFit.contain,
+                        ),
+                      ),
+                      SizedBox(height: 20.w),
+                      _buildAvatarCard(),
+                      SizedBox(height: 12.w),
+                      _buildZodiacDesc(),
+                      SizedBox(height: 20.w),
+                      buildCharacterList(),
+                      SizedBox(height: 70.w),
+                      buildOpenNowButton(),
+
+                    ],
+                  ),
+                ),
+              ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+
+  Widget _buildZodiacDesc() {
+    return Obx(() {
+      if (controller.keyboardMemeExplain.value.meme == null ||
+          controller.keyboardMemeExplain.value.explain == null) {
+        return SizedBox(width: 278.w);
+      }
+      return Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Stack(
+            alignment: Alignment.center,
+            children: [
+              Container(
+                alignment: Alignment.center,
+                padding: EdgeInsets.only(
+                  left: 19.w,
+                  right: 19.w,
+                  top: 7.w,
+                  bottom: 7.w,
+                ),
+                decoration: ShapeDecoration(
+                  color: const Color(0x28B70080),
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(16.r),
+                  ),
+                ),
+                child: SizedBox(
+                  width: 278.w,
+                  child: Text(
+                    "${controller.keyboardMemeExplain.value.meme ?? ""}\n${controller.keyboardMemeExplain.value.explain ?? ""}",
+                    style: TextStyle(
+                      color: Colors.white.withAlpha(229),
+                      fontSize: 12.sp,
+                      fontWeight: FontWeight.w500,
+                    ),
+                  ),
+                ),
+              ),
+              Positioned(
+                top: 0.w,
+                left: 8.w,
+                child: Assets.images.iconNewUserZodiacLeft.image(
+                  width: 16.w,
+                  height: 16.w,
+                  fit: BoxFit.contain,
+                ),
+              ),
+              Positioned(
+                right: 8.w,
+                bottom: 0.w,
+                child: Assets.images.iconNewUserZodiacRight.image(
+                  width: 16.w,
+                  height: 16.w,
+                  fit: BoxFit.contain,
+                ),
+              ),
+            ],
+          ),
+        ],
+      );
+    });
+  }
+
+  Widget _buildAvatarCard() {
+    return Stack(
+      alignment: Alignment.center,
+      children: [
+        Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Column(
+              children: [
+                SizedBox(height: 20.w),
+                CircleAvatarWidget(
+                  image: Assets.images.iconKeyboardDefaultAvatar.provider(),
+                  imageUrl: controller.userInfo?.imageUrl,
+                  size: 88.w,
+                  borderColor: Colors.white,
+                  borderWidth: 2.r,
+                  placeholder: (_, __) => const SizedBox.shrink(),
+                ),
+                SizedBox(height: 14.w),
+                Obx(() {
+                  final zodiac = controller.zodiacLabelFromUserInfo;
+                  if (zodiac != null) {
+                    return Row(
+                      children: [
+                        zodiac.image.image(width: 14.w, height: 14.w),
+                        SizedBox(width: 4.w),
+                        Text(
+                          zodiac.name,
+                          style: TextStyle(
+                            color: Colors.white,
+                            fontSize: 14.97.sp,
+                            fontWeight: FontWeight.w700,
+                          ),
+                        ),
+                      ],
+                    );
+                  } else {
+                    return const SizedBox.shrink();
+                  }
+                }),
+              ],
+            ),
+            Lottie.asset(
+              Assets.anim.animNewUserData,
+              repeat: true,
+              width: 131.w,
+              fit: BoxFit.contain,
+            ),
+            Column(
+              children: [
+                SizedBox(height: 20.w),
+                CircleAvatarWidget(
+                  image: Assets.images.iconKeyboardDefaultAvatar.provider(),
+                  imageUrl: controller.newUserKeyboardInfo.imageUrl,
+                  size: 88.w,
+                  borderColor: Colors.white,
+                  borderWidth: 2.r,
+                  placeholder: (_, __) => const SizedBox.shrink(),
+                ),
+                SizedBox(height: 14.w),
+                Obx(() {
+                  final zodiac = controller.zodiacLabelFromNewUserKeyboardInfo;
+                  if (zodiac != null) {
+                    return Row(
+                      children: [
+                        zodiac.image.image(width: 14.w, height: 14.w),
+                        SizedBox(width: 4.w),
+                        Text(
+                          zodiac.name,
+                          style: TextStyle(
+                            color: Colors.white,
+                            fontSize: 14.97.sp,
+                            fontWeight: FontWeight.w700,
+                          ),
+                        ),
+                      ],
+                    );
+                  } else {
+                    return const SizedBox.shrink();
+                  }
+                }),
+              ],
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  Widget buildCharacterList() {
+    return Obx(() {
+      if (controller.charactersList.isEmpty) {
+        return const SizedBox.shrink();
+      }
+      return Container(
+        padding: EdgeInsets.only(top: 20.w),
+        decoration: ShapeDecoration(
+          color: Colors.white,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(28.r),
+          ),
+        ),
+        margin: EdgeInsets.only(left: 21.w, right: 21.w),
+
+        child: Column(
+          children: [
+            Row(
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Assets.images.iconNewUserResultLoveLeft.image(
+                  width: 12.w,
+                  height: 12.w,
+                  fit: BoxFit.contain,
+                ),
+                Text(
+                  '${StringName.newUserResultIntimacyTitle} ${controller.newUserKeyboardInfo.intimacy ?? "0"}%',
+                  textAlign: TextAlign.center,
+                  style: TextStyle(
+                    color: Colors.black.withAlpha(204),
+                    fontSize: 14.sp,
+                    fontWeight: FontWeight.w700,
+                  ),
+                ),
+                Assets.images.iconNewUserResultLoveRight.image(
+                  width: 12.w,
+                  height: 12.w,
+                  fit: BoxFit.contain,
+                ),
+              ],
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Text(
+                  StringName.newUserResultIntimacyDesc,
+                  textAlign: TextAlign.center,
+                  style: TextStyle(
+                    color: Colors.black.withAlpha(128),
+                    fontSize: 12.sp,
+                    fontWeight: FontWeight.w400,
+                  ),
+                ),
+              ],
+            ),
+            SizedBox(height: 26.w),
+            SizedBox(
+              height: 100.w,
+              child: AutoScrollListView(
+                itemCount: (controller.charactersList.length / 2).ceil(),
+                scrollDirection: Axis.horizontal,
+                itemBuilder: (context, columnIndex) {
+                  int rowCount = 2;
+                  int startIndex = columnIndex * rowCount;
+                  final List<CharacterInfo> columnItems =
+                      controller.charactersList
+                          .skip(startIndex)
+                          .take(rowCount)
+                          .toList();
+                  return Column(
+                    children:
+                        columnItems.map((item) {
+                          final emoji = item.emoji ?? "";
+                          final name = item.name ?? "";
+                          return Padding(
+                            padding: EdgeInsets.symmetric(
+                              vertical: 4.w,
+                              horizontal: 4.w,
+                            ),
+                            child: Obx(() {
+                              final isSelected = controller.selectCharactersList
+                                  .any(
+                                    (selected) => selected.name == item.name,
+                                  );
+                              return SizedBox(
+                                height: 36.w,
+                                child: ChoiceChip(
+                                  label: Text(
+                                    "$emoji$name",
+                                    style: TextStyle(
+                                      color:
+                                          isSelected
+                                              ? Colors.white
+                                              : Colors.black.withAlpha(204),
+                                      fontSize: 13.sp,
+                                      fontWeight: FontWeight.w400,
+                                    ),
+                                  ),
+                                  showCheckmark: false,
+                                  selected: isSelected,
+                                  selectedColor: const Color(0xFFB782FF),
+                                  backgroundColor: Color(0xFFF6F5FA),
+                                  shape: RoundedRectangleBorder(
+                                    borderRadius: BorderRadius.circular(31.r),
+                                  ),
+                                  side: BorderSide(
+                                    width: 0.w,
+                                    color: Colors.transparent
+                                  ),
+                                  onSelected: (selected) {
+                                    if (selected != isSelected) {
+                                      controller.onSelected(item);
+                                    }
+                                  },
+                                ),
+                              );
+                            }),
+                          );
+                        }).toList(),
+                  );
+                },
+              ),
+            ),
+            SizedBox(height: 26.w),
+          ],
+        ),
+      );
+    });
+  }
+
+  Widget buildOpenNowButton() {
+    return Column(
+      children: [
+        Assets.images.iconNewUserResultOpenDesc.image(
+          width: 126.w,
+          height: 18.w,
+          fit: BoxFit.contain,
+        ),
+        SizedBox(height: 18.w),
+        GestureDetector(
+          onTap: () {
+            controller.clickOpenNow();
+          },
+          child: Assets.images.iconNewUserOpenNow.image(
+            width: 256.w,
+            height: 79.w,
+            fit: BoxFit.contain,
+          ),
+        ),
+      ],
+    );
+  }
+}

+ 32 - 0
lib/module/new_user/step/birthday/step_birthday_logic.dart

@@ -0,0 +1,32 @@
+import 'package:get/get.dart';
+import 'package:intl/intl.dart';
+import '../../../../resource/assets.gen.dart';
+import '../../../../utils/age_zodiac_sign_util.dart';
+
+mixin StepBirthdayLogic {
+  final tag = "StepBirthdayLogic";
+  DateTime initialDate = DateTime.now();
+
+  // 最小日期
+  var minimumDate = DateTime.parse("1921-01-01");
+
+
+  //星座
+  Rx<Zodiac> constellation = Rx<Zodiac>(AgeZodiacSignUtil.getZodiacSign(DateTime.now()));
+
+  var age = 0.obs;
+
+  final Rxn<DateTime> _currentBirthday = Rxn<DateTime>(null);
+
+  DateTime? get currentBirthday => _currentBirthday.value;
+
+  updateConstellation(DateTime date) {
+    constellation.value = AgeZodiacSignUtil.getZodiacSign(date);
+    age.value = AgeZodiacSignUtil.calculateAge(date);
+    _currentBirthday.value = date;
+    print("星座:${constellation.value}");
+    print("年龄:${age.value}");
+    print("日期:${date}");
+
+  }
+}

+ 153 - 0
lib/module/new_user/step/birthday/step_birthday_view.dart

@@ -0,0 +1,153 @@
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:keyboard/module/new_user/new_user_controller.dart';
+import 'package:get/get.dart';
+import 'package:flutter/material.dart';
+
+import '../../../../resource/assets.gen.dart';
+import '../../../../resource/string.gen.dart';
+import '../../../../utils/styles.dart';
+import '../../../../widget/birthday_date_picker.dart';
+
+class StepBirthdayView extends BaseView<NewUserController> {
+  const StepBirthdayView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Container(
+          margin: EdgeInsets.only(left: 30.w, right: 30.w),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Row(children: [
+                Text(
+                  StringName.newUserBirthdayTitle,
+                  style: TextStyle(
+                    color: Colors.black.withAlpha(204),
+                    fontSize: 22.sp,
+                    fontWeight: FontWeight.w500,
+                    height: 0,
+                  ),
+                ),
+                Assets.images.iconNewUserBirthdayLogo.image(
+                  width: 50.w,
+                  height: 36.w,
+                  fit: BoxFit.contain,
+                ),
+              ],),
+              SizedBox(height: 7.h),
+              Text(
+                StringName.newUserBirthdayDesc,
+                style: TextStyle(
+                  color: Colors.black.withAlpha(153),
+                  fontSize: 14.sp,
+                  fontWeight: FontWeight.w400,
+                ),
+              ),
+            ],
+          ),
+        ),
+        SizedBox(height: 72.w),
+        _buildContent(context),
+      ],
+    );
+  }
+
+  _buildContent(BuildContext context) {
+    return Column(
+      children: [
+        Container(child: _datePicker(context)),
+        SizedBox(height: 20.h),
+        Obx(() {
+          return Row(
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: [
+              // 星座
+              Container(
+                width: 26.r,
+                height: 26.r,
+                decoration: ShapeDecoration(
+                  color: Colors.white,
+                  shape: OvalBorder(
+                    side: BorderSide(width: 1.r, color: Colors.white),
+                  ),
+                  shadows: [
+                    BoxShadow(
+                      color: Color(0x30A46EFE),
+                      blurRadius: 2.r,
+                      offset: Offset(0, 2),
+                      spreadRadius: 0,
+                    ),
+                  ],
+                ),
+                child: Container(
+                  margin: EdgeInsets.all(2.r),
+                  width: 22.r,
+                  height: 22.r,
+                  decoration: ShapeDecoration(
+                    gradient: LinearGradient(
+                      begin: Alignment.centerLeft,
+                      end: Alignment.centerRight,
+                      colors: [
+                        const Color(0xFF7D46FC),
+                        const Color(0xFFBC87FF),
+                      ],
+                    ),
+                    shape: OvalBorder(),
+                    shadows: [
+                      BoxShadow(
+                        color: Color(0x30A46EFE),
+                        blurRadius: 2.r,
+                        offset: Offset(0, 2.08),
+                        spreadRadius: 0,
+                      ),
+                    ],
+                  ),
+                  child: Center(
+                    child: controller.constellation.value.image.image(
+                      width: 14.r,
+                      height: 14.r,
+                      fit: BoxFit.contain,
+                    ),
+                  ),
+                ),
+              ),
+              SizedBox(width: 7.w),
+
+              Text(
+                controller.constellation.value.name,
+                style: Styles.getTextStyleBlack204W500(14.sp),
+              ),
+            ],
+          );
+        }),
+      ],
+    );
+  }
+
+  SizedBox _datePicker(BuildContext context) {
+    var widget = SizedBox(
+      height: 150.h,
+      width: 320.w,
+      // child: DatePickerWidget(),
+      child: BirthdayDatePicker(
+        initialDate: controller.initialDate,
+        minimumDate: controller.minimumDate,
+        maximumDate: DateTime.now(),
+
+        onDateChanged: (date) {
+          controller.updateConstellation(date);
+        },
+      ),
+    );
+    return widget;
+  }
+}

+ 16 - 0
lib/module/new_user/step/gender/step_gender_logic.dart

@@ -0,0 +1,16 @@
+import 'package:get/get.dart';
+
+mixin StepGenDerLogic {
+  final tag = "StepGenDerLogic";
+
+  final RxnInt _currentGender = RxnInt(null);
+
+  int? get currentGender => _currentGender.value;
+
+  changeGender(int genderValue) {
+    _currentGender.value =
+        _currentGender.value == genderValue
+            ? (genderValue == 1 ? 2 : 1)
+            : genderValue;
+  }
+}

+ 204 - 0
lib/module/new_user/step/gender/step_gender_view.dart

@@ -0,0 +1,204 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:keyboard/resource/string.gen.dart';
+
+import '../../../../resource/assets.gen.dart';
+import '../../new_user_controller.dart';
+
+class StepGenderView extends BaseView<NewUserController> {
+  const StepGenderView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Container(
+          margin: EdgeInsets.only(left: 30.w, right: 30.w),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Text(
+                StringName.newUserGenderTitle,
+                style: TextStyle(
+                  color: Colors.black.withAlpha(204),
+                  fontSize: 22.sp,
+                  fontWeight: FontWeight.w500,
+                  height: 0,
+                ),
+              ),
+              SizedBox(height: 7.h),
+              Text(
+                StringName.newUserGenderDesc,
+                style: TextStyle(
+                  color: Colors.black.withAlpha(153),
+                  fontSize: 14.sp,
+                  fontWeight: FontWeight.w400,
+                ),
+              ),
+            ],
+          ),
+        ),
+        SizedBox(height: 72.w),
+        Container(
+          alignment: Alignment.center,
+          child: Column(
+            children: [
+              _buildGenderSelector(
+                genderValue: 2,
+                title: '女生',
+                subtitle: 'Girl',
+                logo: Assets.images.iconChangeGenderFemaleLogo.image(
+                  width: 20.w,
+                  height: 20.w,
+                ),
+                selectedImage: Assets.images.iconChangeGenderFemaleSelect.image(
+                  width: 320.w,
+                  fit: BoxFit.fill,
+                ),
+                unselectedImage: Assets.images.iconChangeGenderFemaleUnselect
+                    .image(width: 320.w, fit: BoxFit.cover),
+                isRightAligned: true,
+              ),
+
+              SizedBox(height: 25.h),
+
+              _buildGenderSelector(
+                genderValue: 1,
+                title: '男生',
+                subtitle: 'Boy',
+                logo: Assets.images.iconChangeGenderMaleLogo.image(
+                  width: 20.w,
+                  height: 20.w,
+                ),
+                selectedImage: Assets.images.iconChangeGenderMaleSelect.image(
+                  width: 320.w,
+                  fit: BoxFit.fill,
+                ),
+                unselectedImage: Assets.images.iconChangeGenderMaleUnselect
+                    .image(width: 320.w, fit: BoxFit.cover),
+                isRightAligned: false,
+              ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+
+  Widget _buildGenderSelector({
+    required int genderValue, // 1: 男生, 2: 女生
+    required String title,
+    required String subtitle,
+    required Widget logo,
+    required Image selectedImage,
+    required Image unselectedImage,
+    required bool isRightAligned,
+  }) {
+    return GestureDetector(
+      onTap: () {
+        controller.changeGender(genderValue);
+      },
+      child: Obx(() {
+        final isSelected = controller.currentGender == genderValue;
+        return SizedBox(
+          width: 320.w,
+          height: 115.h,
+          child: Stack(
+            clipBehavior: Clip.none,
+            children: [
+              isSelected ? selectedImage : unselectedImage,
+              Container(
+                alignment:
+                    isRightAligned
+                        ? Alignment.centerRight
+                        : Alignment.centerLeft,
+                padding: EdgeInsets.only(
+                  right: isRightAligned ? 40.w : 0,
+                  left: isRightAligned ? 0 : 40.w,
+                ),
+                child: Column(
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: [
+                    Row(
+                      mainAxisSize: MainAxisSize.min,
+                      children:
+                          isRightAligned
+                              ? [
+                                logo,
+                                SizedBox(width: 4.w),
+                                Text(
+                                  title,
+                                  style: TextStyle(
+                                    color:
+                                        isSelected
+                                            ? Colors.black.withAlpha(204)
+                                            : Colors.black.withAlpha(102),
+                                    fontSize: 22.sp,
+                                    fontWeight: FontWeight.w700,
+                                  ),
+                                ),
+                              ]
+                              : [
+                                Text(
+                                  title,
+                                  style: TextStyle(
+                                    color:
+                                        isSelected
+                                            ? Colors.black.withAlpha(204)
+                                            : Colors.black.withAlpha(102),
+                                    fontSize: 22.sp,
+                                    fontWeight: FontWeight.w700,
+                                  ),
+                                ),
+                                SizedBox(width: 4.w),
+                                logo,
+                              ],
+                    ),
+                    SizedBox(height: 4.w),
+                    Row(
+                      mainAxisSize: MainAxisSize.min,
+                      children:
+                          isRightAligned
+                              ? [
+                                SizedBox(width: 20.w, height: 20.w),
+                                SizedBox(width: 4.w),
+                                Text(
+                                  subtitle,
+                                  style: TextStyle(
+                                    color: Colors.black.withAlpha(128),
+                                    fontSize: 14.sp,
+                                    fontWeight: FontWeight.w400,
+                                  ),
+                                ),
+                              ]
+                              : [
+                                Text(
+                                  subtitle,
+                                  style: TextStyle(
+                                    color: Colors.black.withAlpha(128),
+                                    fontSize: 14.sp,
+                                    fontWeight: FontWeight.w400,
+                                  ),
+                                ),
+                                SizedBox(width: 4.w),
+                                SizedBox(width: 20.w, height: 20.w),
+                              ],
+                    ),
+                  ],
+                ),
+              ),
+            ],
+          ),
+        );
+      }),
+    );
+  }
+}

+ 26 - 0
lib/module/new_user/step/intimacy/step_intimacy_stages_logic.dart

@@ -0,0 +1,26 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+import '../../../../utils/intimacy_util.dart';
+
+mixin StepIntimacyStagesLogic {
+  final tag = "StepIntimacyStagesLogic";
+
+  final selectedIndex = 2.obs;
+
+  int get currentIntimacyMedian => IntimacyUtil.getMedianByIndex(selectedIndex.value);
+
+
+  final List<String> stages = [
+    '初相识',
+    '追爱期ing',
+    '暧昧期ing',
+    '热恋期',
+    '伴侣',
+    '灵魂伴侣',
+  ];
+  void selectIndex(int index) {
+    selectedIndex.value = index;
+  }
+
+}

+ 128 - 0
lib/module/new_user/step/intimacy/step_intimacy_stages_view.dart

@@ -0,0 +1,128 @@
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/module/new_user/new_user_controller.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/resource/string.gen.dart';
+import 'package:get/get.dart';
+
+import '../../../../resource/assets.gen.dart';
+
+class StepIntimacyStagesView extends BasePage<NewUserController> {
+  const StepIntimacyStagesView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Container(
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Container(
+            margin: EdgeInsets.only(left: 30.w, right: 30.w),
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                Text(
+                  StringName.newUserIntimacyStagesTitle,
+                  style: TextStyle(
+                    color: Colors.black.withAlpha(204),
+                    fontSize: 22.sp,
+                    fontWeight: FontWeight.w500,
+                    height: 0,
+                  ),
+                ),
+                SizedBox(height: 7.h),
+                Text(
+                  StringName.newUserIntimacyStagesDesc,
+                  style: TextStyle(
+                    color: Colors.black.withAlpha(153),
+                    fontSize: 14.sp,
+                    fontWeight: FontWeight.w400,
+                  ),
+                ),
+              ],
+            ),
+          ),
+          SizedBox(height: 40.w),
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              Assets.images.iconNewUserStagesAmbiguous.image(
+                width: 211.w,
+                height: 339.w,
+                fit: BoxFit.contain,
+              ),
+              _buildStagesList(),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildStagesList() {
+    return Container(
+      margin: EdgeInsets.only(right: 18.w),
+      child: Obx(() {
+        return Column(
+          mainAxisSize: MainAxisSize.min,
+          children: List.generate(controller.stages.length, (index) {
+            final isSelected = controller.selectedIndex.value == index;
+            return GestureDetector(
+              onTap: () => controller.selectIndex(index),
+              child: Container(
+                height: 64.h,
+                width: 120.w,
+                alignment: Alignment.center,
+                child: AnimatedScale(
+                  scale: isSelected ? 1.0 : 0.82,
+                  duration: const Duration(milliseconds: 250),
+                  curve: Curves.easeOutCubic,
+                  child: Container(
+                    alignment: Alignment.center,
+                    decoration: BoxDecoration(
+                      color: isSelected ? null : Colors.white,
+                      gradient: isSelected
+                          ? LinearGradient(
+                        begin: Alignment(0.04, 0.21),
+                        end: Alignment(0.98, 0.76),
+                        colors: [
+                          const Color(0xFF7D46FC),
+                          const Color(0xFFBC87FF),
+                        ],
+                      )
+                          : null,
+                      boxShadow: isSelected
+                          ? [
+                        BoxShadow(
+                          color: const Color(0xFFCEBEFF),
+                          blurRadius: 5.80,
+                          offset: Offset(0, 4),
+                        ),
+                      ]
+                          : [],
+                      borderRadius: BorderRadius.circular(17.r),
+                    ),
+                    child: Text(
+                      controller.stages[index],
+                      style: TextStyle(
+                        color: isSelected ? Colors.white : Colors.black.withAlpha(153),
+                        fontSize: 17.14.sp,
+                        fontWeight: FontWeight.w500,
+                      ),
+                    ),
+                  ),
+                ),
+              ),
+            );
+          }),
+        );
+      }),
+    );
+  }
+
+}

+ 10 - 0
lib/module/new_user/step/nickname/step_nickname_logic.dart

@@ -0,0 +1,10 @@
+import 'package:get/get.dart';
+import 'package:flutter/material.dart';
+mixin StepNicknameLogic {
+  final tag = "StepNicknameLogic";
+
+  final RxString nickname = "".obs;
+
+  final TextEditingController textEditingController = TextEditingController();
+
+}

+ 106 - 0
lib/module/new_user/step/nickname/step_nickname_view.dart

@@ -0,0 +1,106 @@
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:keyboard/module/new_user/new_user_controller.dart';
+import 'package:flutter/material.dart';
+
+import '../../../../resource/string.gen.dart';
+class StepNicknameView extends BaseView<NewUserController>{
+  const StepNicknameView({super.key});
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Container(
+          margin: EdgeInsets.only(left: 30.w, right: 30.w),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Text(
+                StringName.newUserNicknameTitle,
+                style: TextStyle(
+                  color: Colors.black.withAlpha(204),
+                  fontSize: 22.sp,
+                  fontWeight: FontWeight.w500,
+                  height: 0,
+                ),
+              ),
+              SizedBox(height: 7.h),
+              Text(
+                StringName.newUserNicknameDesc,
+                style: TextStyle(
+                  color: Colors.black.withAlpha(153),
+                  fontSize: 14.sp,
+                  fontWeight: FontWeight.w400,
+                ),
+              ),
+            ],
+          ),
+        ),
+        SizedBox(height: 72.w),
+        _buildContent(context),
+      ],
+    );
+  }
+
+
+  Widget _buildContent(BuildContext context) {
+    return    Container(
+      margin: EdgeInsets.only( left: 27.w, right: 27.w),
+      alignment: Alignment.center,
+      child: Container(
+        height: 48.h,
+        alignment: Alignment.center,
+        child: TextField(
+          controller: controller.textEditingController,
+          // maxLength: maxLength,
+          maxLines: 1,
+          onChanged: (value) {
+            controller.nickname.value = value;
+          },
+          cursorColor: Color(0xFF7B7DFF),
+          textAlign: TextAlign.center,
+          textAlignVertical: TextAlignVertical.center,
+          decoration: InputDecoration(
+            counterText: "",
+            hintText: "请输入你的昵称",
+            hintStyle: TextStyle(
+              color: Colors.black.withAlpha(77),
+              fontSize: 18.sp,
+              fontWeight: FontWeight.w500,
+            ),
+            border: UnderlineInputBorder(
+              borderSide: BorderSide(
+                color: Colors.black.withAlpha(26),
+                width: 1.w,
+              ),
+            ),
+            focusedBorder: UnderlineInputBorder(
+              borderSide: BorderSide(
+                color: Colors.black.withAlpha(26),
+                width: 1.w,
+              ),
+            ),
+            enabledBorder: UnderlineInputBorder(
+              borderSide: BorderSide(
+                color: Colors.black.withAlpha(26),
+                width: 1.w,
+              ),
+            ),
+            filled: true,
+            fillColor: Colors.transparent,
+          ),
+        ),
+      ),
+    );
+  }
+
+
+
+}

+ 103 - 0
lib/module/new_user/step/partner/step_partner_logic.dart

@@ -0,0 +1,103 @@
+import 'package:get/get.dart';
+import 'package:keyboard/utils/toast_util.dart';
+
+import '../../../../data/bean/default_avatar_info.dart';
+import '../../../../data/repository/config_repository.dart';
+import '../../../../dialog/select_birthday_dialog.dart';
+import '../../../../utils/age_zodiac_sign_util.dart';
+import '../../../../utils/atmob_log.dart';
+
+mixin StepPartnerLogic {
+  final tag = "StepPartnerLogic";
+
+  final ConfigRepository configRepository = ConfigRepository.getInstance();
+
+  Rxn<DefaultAvatarInfo> get currentDefaultAvatarInfo =>
+      configRepository.defaultAvatarInfo;
+
+  final RxString _partnerAvatarUrl = "".obs;
+
+  String get partnerAvatarUrl => _partnerAvatarUrl.value;
+
+  final partnerName = "".obs;
+
+  final RxnInt _partnerGender = RxnInt(null);
+
+  int? get partnerGender => _partnerGender.value;
+
+  final RxList<String> _girlAvatars = <String>[].obs;
+
+  final RxList<String> _boyAvatars = <String>[].obs;
+
+  final RxString _currentPartnerDate = "".obs;
+
+  String get currentPartnerDate => _currentPartnerDate.value;
+
+  final Rxn<DateTime> _currentPartnerBirthday = Rxn<DateTime>(null);
+
+  DateTime? get currentPartnerBirthday => _currentPartnerBirthday.value;
+
+
+  DateComponents get dateComponents =>
+      DateComponents.fromDateTime(_currentPartnerBirthday.value!);
+
+
+  void nextAvatar() {
+    AtmobLog.d(tag, "nextAvatar");
+
+    if (_partnerGender.value == 1) {
+      int currentIndex = _boyAvatars.indexOf(_partnerAvatarUrl.value);
+      _partnerAvatarUrl.value = _boyAvatars[(currentIndex + 1) % _boyAvatars.length];
+    } else {
+      int currentIndex = _girlAvatars.indexOf(_partnerAvatarUrl.value);
+      _partnerAvatarUrl.value = _girlAvatars[(currentIndex + 1) % _girlAvatars.length];
+    }
+  }
+
+  void updateAvatarListsAndSelectFirst(DefaultAvatarInfo? info) {
+    _boyAvatars.assignAll(info?.maleAvatars ?? []);
+    _girlAvatars.assignAll(info?.femaleAvatars ?? []);
+    if (_partnerGender.value == 1) {
+      _partnerAvatarUrl.value = _boyAvatars.isNotEmpty ? _boyAvatars.first : "";
+    } else {
+      _partnerAvatarUrl.value = _girlAvatars.isNotEmpty ? _girlAvatars.first : "";
+    }
+  }
+
+  void onNicknameChange(String name) {
+    AtmobLog.d(tag, "onNicknameChange: $name");
+    if (name.isNotEmpty) {
+      partnerName.value = name;
+    } else {
+      partnerName.value = "";
+    }
+  }
+
+  void onPartnerGenderChanged(int gender) {
+    AtmobLog.d(tag, gender.toString());
+    _partnerGender.value = gender;
+
+    // 切换性别时自动切换对应性别的第一个头像
+    if (gender == 1 && _boyAvatars.isNotEmpty) {
+      _partnerAvatarUrl.value = _boyAvatars.first;
+    } else if (_girlAvatars.isNotEmpty) {
+      _partnerAvatarUrl.value = _girlAvatars.first;
+    } else {
+      _partnerAvatarUrl.value = "";
+    }
+
+  }
+
+  void changePartnerBirthday() {
+
+    SelectBirthdayDialog.show(
+      initialDate: _currentPartnerBirthday.value ?? DateTime.now(),
+      minimumDate: DateTime.parse("1921-01-01"),
+      onDateChanged: (dataTime) {
+        _currentPartnerBirthday.value = dataTime;
+      },
+    );
+  }
+
+
+}

+ 395 - 0
lib/module/new_user/step/partner/step_partner_view.dart

@@ -0,0 +1,395 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:keyboard/module/new_user/new_user_controller.dart';
+import 'package:flutter/material.dart';
+import 'package:keyboard/resource/string.gen.dart';
+import 'package:get/get.dart';
+
+import '../../../../resource/assets.gen.dart';
+import '../../../../resource/colors.gen.dart';
+import '../../../../utils/age_zodiac_sign_util.dart';
+import '../../../../utils/styles.dart';
+import '../../../../widget/avatar/avatar_image_widget.dart';
+
+class StepPartnerView extends BaseView<NewUserController> {
+  const StepPartnerView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Container(
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          _buildStepTitle(),
+          SizedBox(height: 24.w),
+          Center(child: _buildAvatar()),
+          SizedBox(height: 12.w),
+          Expanded(child: SingleChildScrollView(child: _buildContent())),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildStepTitle() {
+    return Container(
+      margin: EdgeInsets.only(left: 30.w, right: 30.w),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Text(
+            StringName.newUserPartnerTitle,
+            style: TextStyle(
+              color: Colors.black.withAlpha(204),
+              fontSize: 22.sp,
+              fontWeight: FontWeight.w500,
+            ),
+          ),
+          SizedBox(height: 7.h),
+          Text(
+            StringName.newUserPartnerDesc,
+            style: TextStyle(
+              color: Colors.black.withAlpha(153),
+              fontSize: 14.sp,
+              fontWeight: FontWeight.w400,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildAvatar() {
+    return GestureDetector(
+      onTap: controller.nextAvatar,
+      child: Obx(() {
+        return Container(
+          height: 90.w,
+          child: Stack(
+            children: [
+              Container(
+                width: 84.w,
+                height: 84.w,
+                child:
+                    controller.partnerAvatarUrl.isNotEmpty
+                        ? CircleAvatarWidget(
+                          image:
+                              Assets.images.iconKeyboardDefaultAvatar
+                                  .provider(),
+                          imageUrl: controller.partnerAvatarUrl,
+                          size: 84.w,
+                          borderColor: Colors.white,
+                          borderWidth: 2.r,
+                          placeholder: (_, __) {
+                            return const CupertinoActivityIndicator();
+                          },
+                        )
+                        : SizedBox(),
+              ),
+              Positioned(
+                left: 0,
+                right: 0,
+                bottom: 0,
+                child: _buildAvatarSwitch(),
+              ),
+            ],
+          ),
+        );
+      }),
+    );
+  }
+
+  Widget _buildAvatarSwitch() {
+    return GestureDetector(
+      onTap: controller.nextAvatar,
+      child: SizedBox(
+        width: 22.r,
+        height: 22.r,
+        child: Assets.images.iconCharacterCustomDetailSwitch.image(
+          width: 22.r,
+          height: 22.r,
+        ),
+      ),
+    );
+  }
+
+  Widget _buildContent() {
+    return Container(
+      margin: EdgeInsets.only(left: 16.w, right: 16.w, bottom: 200.w),
+      padding: EdgeInsets.only(top: 21.w),
+      decoration: BoxDecoration(
+        borderRadius: BorderRadius.circular(18),
+        color: Colors.white,
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Padding(
+            padding: EdgeInsets.only(left: 20.w),
+            child: Text(
+              StringName.newUserPartnerIs,
+              style: Styles.getTextStyleBlack204W500(14.sp),
+            ),
+          ),
+          SizedBox(height: 12.w),
+          buildPartnerNameFiled(),
+          SizedBox(height: 30.w),
+          Padding(
+            padding: EdgeInsets.only(left: 20.w),
+            child: Text(
+              StringName.gender,
+              style: Styles.getTextStyleBlack204W500(14.sp),
+            ),
+          ),
+          SizedBox(height: 9.w),
+          Obx(
+            () => Container(
+              margin: EdgeInsets.symmetric(horizontal: 16.w),
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.spaceAround,
+                children: [
+                  // 女生按钮
+                  Expanded(
+                    child: buildGenderSwitchButton(
+                      isSelected: controller.partnerGender == 2,
+                      text: StringName.newUserPartnerIsFemale,
+                      onTap: () => controller.onPartnerGenderChanged(2),
+                    ),
+                  ),
+                  SizedBox(width: 16.w),
+                  // 男生按钮
+                  Expanded(
+                    child: buildGenderSwitchButton(
+                      isSelected: controller.partnerGender == 1,
+                      text: StringName.newUserPartnerIsMale,
+                      onTap: () => controller.onPartnerGenderChanged(1),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ),
+
+          SizedBox(height: 30.w),
+          Padding(
+            padding: EdgeInsets.only(left: 20.w),
+            child: Text(
+              StringName.newUserBirthdayTitle,
+              style: Styles.getTextStyleBlack204W500(14.sp),
+            ),
+          ),
+          SizedBox(height: 12.w),
+
+          _buildPartnerBirthday(),
+          SizedBox(height: 32.w),
+        ],
+      ),
+    );
+  }
+
+  Widget buildPartnerNameFiled() {
+    return Container(
+      height: 42.w,
+      margin: EdgeInsets.symmetric(horizontal: 16.w),
+      padding: EdgeInsets.symmetric(horizontal: 12.w),
+      decoration: BoxDecoration(
+        color: Color(0XFFF6F5FA),
+        borderRadius: BorderRadius.circular(28.w),
+      ),
+      child: Row(
+        children: [
+          Expanded(
+            child: TextField(
+              cursorHeight: 20.w,
+              style: TextStyle(
+                fontSize: 14.sp,
+                color: Colors.black.withAlpha(204),
+                fontWeight: FontWeight.w500,
+              ),
+              maxLines: 1,
+              textAlignVertical: TextAlignVertical.center,
+              textInputAction: TextInputAction.next,
+              decoration: InputDecoration(
+                hintText: StringName.newUserNicknameTitle,
+                counterText: '',
+                hintStyle: TextStyle(
+                  fontSize: 14.sp,
+                  color: Colors.black.withAlpha(77),
+                  fontWeight: FontWeight.w500,
+                ),
+                labelStyle: TextStyle(
+                  fontSize: 14.sp,
+                  color: ColorName.primaryTextColor,
+                ),
+                contentPadding: const EdgeInsets.all(0),
+                border: const OutlineInputBorder(borderSide: BorderSide.none),
+                enabled: true,
+              ),
+              onChanged: controller.onNicknameChange,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget buildSwitchPartnerGender() {
+    return Container(
+      width: 296,
+      height: 42,
+      decoration: ShapeDecoration(
+        color: const Color(0xFFF5F4F9),
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)),
+      ),
+    );
+  }
+
+  Widget buildGenderSwitchButton({
+    required String text,
+    required bool isSelected,
+    required VoidCallback onTap,
+  }) {
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        height: 42.w,
+        alignment: Alignment.center,
+        decoration: BoxDecoration(
+          borderRadius: BorderRadius.circular(28.r),
+          color: isSelected ? null : const Color(0xFFF5F4F9),
+          gradient:
+              isSelected
+                  ? LinearGradient(
+                    begin: Alignment.centerLeft,
+                    end: Alignment.centerRight,
+                    transform: GradientRotation(0.5),
+                    colors: [const Color(0xFF7D46FC), const Color(0xFFBC87FF)],
+                  )
+                  : null,
+        ),
+        child: Text(
+          text,
+          style:
+              isSelected
+                  ? Styles.getTextStyleWhiteW500(14.sp)
+                  : Styles.getTextStyleBlack204W500(14.sp),
+        ),
+      ),
+    );
+  }
+
+  _buildPartnerBirthday() {
+    return GestureDetector(
+      onTap: controller.changePartnerBirthday,
+      child: Obx(() {
+        return Container(
+          height: 42.w,
+          margin: EdgeInsets.symmetric(horizontal: 16.w),
+          padding: EdgeInsets.symmetric(horizontal: 12.w),
+          decoration: BoxDecoration(
+            color: Color(0XFFF6F5FA),
+            borderRadius: BorderRadius.circular(28.w),
+          ),
+          child: Row(
+            children: [
+              controller.currentPartnerBirthday == null
+                  ? Expanded(
+                    child: Row(
+                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                      children: [
+                        Text(
+                          StringName.newUserPartnerBirthday,
+                          style: TextStyle(
+                            color: Colors.black.withAlpha(77),
+                            fontSize: 14.sp,
+                            fontWeight: FontWeight.w500,
+                          ),
+                        ),
+                        Assets.images.iconArrowRight.image(
+                          width: 24.w,
+                          height: 24.w,
+                          fit: BoxFit.contain,
+                        ),
+                      ],
+                    ),
+                  )
+                  : Expanded(
+                    child: Row(
+                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                      children: [
+                        _buildBirthdayNotEmptyText(),
+                        Assets.images.iconArrowRight.image(
+                          width: 24.w,
+                          height: 24.w,
+                          fit: BoxFit.contain,
+                        ),
+                      ],
+                    ),
+                  ),
+            ],
+          ),
+        );
+      }),
+    );
+  }
+
+  _buildBirthdayNotEmptyText() {
+    return Row(
+      children: [
+        Text(
+          controller.dateComponents.yearPart,
+          style: Styles.getTextStyleBlack204W500(14.sp),
+        ),
+        SizedBox(width: 4.w),
+        Text(
+          StringName.year,
+          style: TextStyle(
+            color: Colors.black.withAlpha(102),
+            fontSize: 12.sp,
+            fontWeight: FontWeight.w500,
+          ),
+        ),
+        SizedBox(width: 4.w),
+        Text(
+          controller.dateComponents.monthPart,
+          style: Styles.getTextStyleBlack204W500(14.sp),
+        ),
+        SizedBox(width: 4.w),
+        Text(
+          StringName.month,
+          style: TextStyle(
+            color: Colors.black.withAlpha(102),
+            fontSize: 12.sp,
+            fontWeight: FontWeight.w500,
+          ),
+        ),
+        SizedBox(width: 4.w),
+        Text(
+          controller.dateComponents.dayPart,
+          style: Styles.getTextStyleBlack204W500(14.sp),
+        ),
+        Text(
+          StringName.day,
+          style: TextStyle(
+            color: Colors.black.withAlpha(102),
+            fontSize: 12.sp,
+            fontWeight: FontWeight.w500,
+          ),
+        ),
+        SizedBox(width: 16.w),
+        Text(
+          AgeZodiacSignUtil.getZodiacSign(
+            controller.currentPartnerBirthday!,
+          ).name,
+          style: Styles.getTextStyleBlack204W500(14.sp),
+        ),
+      ],
+    );
+  }
+}

+ 77 - 0
lib/resource/assets.gen.dart

@@ -20,6 +20,9 @@ class $AssetsAnimGen {
   String get animIntimacyAnalyseCreatingReportData =>
       'assets/anim/anim_intimacy_analyse_creating_report_data.json';
 
+  /// File path: assets/anim/anim_new_user_data.json
+  String get animNewUserData => 'assets/anim/anim_new_user_data.json';
+
   /// File path: assets/anim/anim_surprise_dialog_data.json
   String get animSurpriseDialogData =>
       'assets/anim/anim_surprise_dialog_data.json';
@@ -40,6 +43,7 @@ class $AssetsAnimGen {
   List<String> get values => [
     animDiscountTicketDialogData,
     animIntimacyAnalyseCreatingReportData,
+    animNewUserData,
     animSurpriseDialogData,
     animTabCharacterSelectedData,
     animTabKeyboardSelectedData,
@@ -176,6 +180,14 @@ class $AssetsImagesGen {
   AssetGenImage get bgMineVipCard =>
       const AssetGenImage('assets/images/bg_mine_vip_card.webp');
 
+  /// File path: assets/images/bg_new_user_result.webp
+  AssetGenImage get bgNewUserResult =>
+      const AssetGenImage('assets/images/bg_new_user_result.webp');
+
+  /// File path: assets/images/bg_new_user_result_intimacy.webp
+  AssetGenImage get bgNewUserResultIntimacy =>
+      const AssetGenImage('assets/images/bg_new_user_result_intimacy.webp');
+
   /// File path: assets/images/bg_profile_edit_intimacy.webp
   AssetGenImage get bgProfileEditIntimacy =>
       const AssetGenImage('assets/images/bg_profile_edit_intimacy.webp');
@@ -416,6 +428,21 @@ class $AssetsImagesGen {
   AssetGenImage get iconCopy =>
       const AssetGenImage('assets/images/icon_copy.webp');
 
+  /// File path: assets/images/icon_custom_character_add_dialog_close.webp
+  AssetGenImage get iconCustomCharacterAddDialogClose => const AssetGenImage(
+    'assets/images/icon_custom_character_add_dialog_close.webp',
+  );
+
+  /// File path: assets/images/icon_custom_character_add_dialog_select.webp
+  AssetGenImage get iconCustomCharacterAddDialogSelect => const AssetGenImage(
+    'assets/images/icon_custom_character_add_dialog_select.webp',
+  );
+
+  /// File path: assets/images/icon_custom_character_add_dialog_title.webp
+  AssetGenImage get iconCustomCharacterAddDialogTitle => const AssetGenImage(
+    'assets/images/icon_custom_character_add_dialog_title.webp',
+  );
+
   /// File path: assets/images/icon_custom_character_add_market.webp
   AssetGenImage get iconCustomCharacterAddMarket => const AssetGenImage(
     'assets/images/icon_custom_character_add_market.webp',
@@ -768,6 +795,42 @@ class $AssetsImagesGen {
   AssetGenImage get iconModeSwitchArrow =>
       const AssetGenImage('assets/images/icon_mode_switch_arrow.webp');
 
+  /// File path: assets/images/icon_new_user_birthday_logo.webp
+  AssetGenImage get iconNewUserBirthdayLogo =>
+      const AssetGenImage('assets/images/icon_new_user_birthday_logo.webp');
+
+  /// File path: assets/images/icon_new_user_open_now.webp
+  AssetGenImage get iconNewUserOpenNow =>
+      const AssetGenImage('assets/images/icon_new_user_open_now.webp');
+
+  /// File path: assets/images/icon_new_user_result_love_left.webp
+  AssetGenImage get iconNewUserResultLoveLeft =>
+      const AssetGenImage('assets/images/icon_new_user_result_love_left.webp');
+
+  /// File path: assets/images/icon_new_user_result_love_right.webp
+  AssetGenImage get iconNewUserResultLoveRight =>
+      const AssetGenImage('assets/images/icon_new_user_result_love_right.webp');
+
+  /// File path: assets/images/icon_new_user_result_open_desc.webp
+  AssetGenImage get iconNewUserResultOpenDesc =>
+      const AssetGenImage('assets/images/icon_new_user_result_open_desc.webp');
+
+  /// File path: assets/images/icon_new_user_result_title.webp
+  AssetGenImage get iconNewUserResultTitle =>
+      const AssetGenImage('assets/images/icon_new_user_result_title.webp');
+
+  /// File path: assets/images/icon_new_user_stages_ambiguous.webp
+  AssetGenImage get iconNewUserStagesAmbiguous =>
+      const AssetGenImage('assets/images/icon_new_user_stages_ambiguous.webp');
+
+  /// File path: assets/images/icon_new_user_zodiac_left.webp
+  AssetGenImage get iconNewUserZodiacLeft =>
+      const AssetGenImage('assets/images/icon_new_user_zodiac_left.webp');
+
+  /// File path: assets/images/icon_new_user_zodiac_right.webp
+  AssetGenImage get iconNewUserZodiacRight =>
+      const AssetGenImage('assets/images/icon_new_user_zodiac_right.webp');
+
   /// File path: assets/images/icon_pisces.webp
   AssetGenImage get iconPisces =>
       const AssetGenImage('assets/images/icon_pisces.webp');
@@ -1006,6 +1069,8 @@ class $AssetsImagesGen {
     bgLoginDialog,
     bgMine,
     bgMineVipCard,
+    bgNewUserResult,
+    bgNewUserResultIntimacy,
     bgProfileEditIntimacy,
     bgProfileLove,
     bgProfileSelected,
@@ -1063,6 +1128,9 @@ class $AssetsImagesGen {
     iconCharacterVip,
     iconConversationAnalysisSampleImage,
     iconCopy,
+    iconCustomCharacterAddDialogClose,
+    iconCustomCharacterAddDialogSelect,
+    iconCustomCharacterAddDialogTitle,
     iconCustomCharacterAddMarket,
     iconCustomDialogClose,
     iconCustomDirectionEditClose,
@@ -1146,6 +1214,15 @@ class $AssetsImagesGen {
     iconMineVipDescArrow,
     iconMineVipOrderArrow,
     iconModeSwitchArrow,
+    iconNewUserBirthdayLogo,
+    iconNewUserOpenNow,
+    iconNewUserResultLoveLeft,
+    iconNewUserResultLoveRight,
+    iconNewUserResultOpenDesc,
+    iconNewUserResultTitle,
+    iconNewUserStagesAmbiguous,
+    iconNewUserZodiacLeft,
+    iconNewUserZodiacRight,
     iconPisces,
     iconProfileAdd,
     iconProfileEdit,

+ 50 - 0
lib/resource/string.gen.dart

@@ -238,6 +238,31 @@ class StringName {
   static final String logoutDialogCancel = 'logout_dialog_cancel'.tr; // 取消
   static final String logoutDialogConfirm = 'logout_dialog_confirm'.tr; // 确认
   static final String logoutDialogDesc = 'logout_dialog_desc'.tr; // 确定退出登录吗?
+  static final String skip = 'skip'.tr; // 跳过
+  static final String newUserGenderTitle = 'new_user_gender_title'.tr; // Hi,欢迎来到追爱小键盘
+  static final String newUserGenderDesc = 'new_user_gender_desc'.tr; // 您的性别是?
+  static final String newUserBirthdayTitle = 'new_user_birthday_title'.tr; // 生日
+  static final String newUserBirthdayDesc = 'new_user_birthday_desc'.tr; // 正确的生日,可以匹配到更合适的内容
+  static final String newUserNicknameTitle = 'new_user_nickname_title'.tr; // 填写昵称
+  static final String newUserNicknameDesc = 'new_user_nickname_desc'.tr; // 起个好听的昵称吧!
+  static final String newUserIntimacyStagesTitle = 'new_user_intimacy_stages_title'.tr; // 当前阶段
+  static final String newUserIntimacyStagesDesc = 'new_user_intimacy_stages_desc'.tr; // 选择适合的阶段,帮你把握回复分寸
+  static final String newUserPartnerTitle = 'new_user_partner_title'.tr; // 完善对方信息
+  static final String newUserPartnerDesc = 'new_user_partner_desc'.tr; // 生成1V1专属恋爱键盘
+  static final String newUserPartnerIs = 'new_user_partner_is'.tr; // TA是
+  static final String newUserPartnerLater = 'new_user_partner_later'.tr; // 稍后设置 使用通用模式
+  static final String newUserPartnerNowGenerate = 'new_user_partner_now_generate'.tr; // 立即生成
+  static final String newUserPartnerBirthday = 'new_user_partner_birthday'.tr; // 请选择出生年月
+  static final String newUserPartnerIsFemale = 'new_user_partner_is_female'.tr; // TA是女生
+  static final String newUserPartnerIsMale = 'new_user_partner_is_male'.tr; // TA是男生
+  static final String year = 'year'.tr; // 年
+  static final String month = 'month'.tr; // 月
+  static final String day = 'day'.tr; // 日
+  static final String selectBirthdayDialogTitle = 'select_birthday_dialog_title'.tr; // 选择日期
+  static final String selectBirthdayDialogCancel = 'select_birthday_dialog_cancel'.tr; // 取消
+  static final String selectBirthdayDialogConfirm = 'select_birthday_dialog_confirm'.tr; // 确定
+  static final String newUserResultIntimacyTitle = 'new_user_result_intimacy_title'.tr; // 亲密度:
+  static final String newUserResultIntimacyDesc = 'new_user_result_intimacy_desc'.tr; // 根据当前的亲密度,建议选择以下人设
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -479,6 +504,31 @@ class StringMultiSource {
       'logout_dialog_cancel': '取消',
       'logout_dialog_confirm': '确认',
       'logout_dialog_desc': '确定退出登录吗?',
+      'skip': '跳过',
+      'new_user_gender_title': 'Hi,欢迎来到追爱小键盘',
+      'new_user_gender_desc': '您的性别是?',
+      'new_user_birthday_title': '生日',
+      'new_user_birthday_desc': '正确的生日,可以匹配到更合适的内容',
+      'new_user_nickname_title': '填写昵称',
+      'new_user_nickname_desc': '起个好听的昵称吧!',
+      'new_user_intimacy_stages_title': '当前阶段',
+      'new_user_intimacy_stages_desc': '选择适合的阶段,帮你把握回复分寸',
+      'new_user_partner_title': '完善对方信息',
+      'new_user_partner_desc': '生成1V1专属恋爱键盘',
+      'new_user_partner_is': 'TA是',
+      'new_user_partner_later': '稍后设置 使用通用模式',
+      'new_user_partner_now_generate': '立即生成',
+      'new_user_partner_birthday': '请选择出生年月',
+      'new_user_partner_is_female': 'TA是女生',
+      'new_user_partner_is_male': 'TA是男生',
+      'year': '年',
+      'month': '月',
+      'day': '日',
+      'select_birthday_dialog_title': '选择日期',
+      'select_birthday_dialog_cancel': '取消',
+      'select_birthday_dialog_confirm': '确定',
+      'new_user_result_intimacy_title': '亲密度:',
+      'new_user_result_intimacy_desc': '根据当前的亲密度,建议选择以下人设',
     },
   };
 }

+ 50 - 35
lib/router/app_pages.dart

@@ -19,6 +19,9 @@ import 'package:keyboard/module/keyboard/keyboard_controller.dart';
 import 'package:keyboard/module/keyboard_manage/keyboard_manage_controller.dart';
 import 'package:keyboard/module/login/login_controller.dart';
 import 'package:keyboard/module/mine/mine_controller.dart';
+import 'package:keyboard/module/new_user/new_user_controller.dart';
+import 'package:keyboard/module/new_user/result/new_user_result_controller.dart';
+import 'package:keyboard/module/new_user/result/new_user_result_page.dart';
 import 'package:keyboard/module/profile/edit/profile_edit_controller.dart';
 import 'package:keyboard/module/profile/profile_controller.dart';
 import 'package:keyboard/module/profile/profile_page.dart';
@@ -54,6 +57,8 @@ import '../module/keyboard_manage/keyboard_manage_page.dart';
 import '../module/login/login_page.dart';
 import '../module/main/main_controller.dart';
 import '../module/main/main_page.dart';
+import '../module/new_user/new_user_page.dart';
+import '../module/new_user/step/gender/step_gender_logic.dart';
 import '../module/profile/edit/profile_edit_page.dart';
 import '../module/store/discount/discount_controller.dart';
 import '../module/store/suprise/goods_surprise_controller.dart';
@@ -93,6 +98,9 @@ abstract class RoutePath {
   static const changeHobbies = '/changeHobbies';
   static const changeCharacters = '/changeCharacters';
   static const userInfo = '/userInfo';
+  static const newUser = '/newUser';
+  static const newUserResult = '/newUserResult';
+
 
   // 图片预览页
   static const imageViewer = '/imageViewerPage';
@@ -134,6 +142,9 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<LoginDialogController>());
     lazyPut(() => getIt.get<ImageViewerController>());
     lazyPut(() => getIt.get<UserInfoController>());
+    lazyPut(() => getIt.get<NewUserController>());
+    lazyPut(() => getIt.get<NewUserResultController>());
+
     lazyPut(() => getIt.get<CustomDirectionEditController>());
   }
 
@@ -143,42 +154,46 @@ class AppBinding extends Bindings {
 }
 
 final generalPages = [
-  GetPage(name: RoutePath.mainTab, page: () => MainPage()),
-  GetPage(name: RoutePath.login, page: () => LoginPage()),
-  GetPage(name: RoutePath.feedback, page: () => FeedbackPage()),
-  GetPage(name: RoutePath.about, page: () => AboutPage()),
-  GetPage(name: RoutePath.browser, page: () => BrowserPage()),
-  GetPage(name: RoutePath.keyboardManage, page: () => KeyboardManagePage()),
-  GetPage(name: RoutePath.characterCustom, page: () => CharacterCustomPage()),
-  GetPage(
-    name: RoutePath.characterCustomDetail,
-    page: () => CharacterCustomDetailPage(),
-  ),
-  GetPage(
-    name: RoutePath.characterCustomList,
-    page: () => CharacterCustomListPage(),
-  ),
-  GetPage(name: RoutePath.store, page: () => StorePage()),
-  GetPage(name: RoutePath.profile, page: () => ProfilePage()),
-  GetPage(name: RoutePath.profileEdit, page: () => ProfileEditPage()),
-  // 键盘引导页
-  GetPage(name: RoutePath.keyboardGuide, page: () => KeyboardGuidePage()),
-  // 亲密度报告页
-  GetPage(name: RoutePath.intimacyAnalyse, page: () => IntimacyAnalysePage()),
-  // 亲密度分析上传页
-  GetPage(
-    name: RoutePath.intimacyAnalyseUpload,
-    page: () => IntimacyAnalyseUploadPage(),
-  ),
+GetPage
+(
+name: RoutePath.mainTab, page: () => MainPage()),
+GetPage(name: RoutePath.login, page: () => LoginPage()),
+GetPage(name: RoutePath.feedback, page: () => FeedbackPage()),
+GetPage(name: RoutePath.about, page: () => AboutPage()),
+GetPage(name: RoutePath.browser, page: () => BrowserPage()),
+GetPage(name: RoutePath.keyboardManage, page: () => KeyboardManagePage()),
+GetPage(name: RoutePath.characterCustom, page: () => CharacterCustomPage()),
+GetPage(
+name: RoutePath.characterCustomDetail,
+page: () => CharacterCustomDetailPage(),
+),
+GetPage(
+name: RoutePath.characterCustomList,
+page: () => CharacterCustomListPage(),
+),
+GetPage(name: RoutePath.store, page: () => StorePage()),
+GetPage(name: RoutePath.profile, page: () => ProfilePage()),
+GetPage(name: RoutePath.profileEdit, page: () => ProfileEditPage()),
+// 键盘引导页
+GetPage(name: RoutePath.keyboardGuide, page: () => KeyboardGuidePage()),
+// 亲密度报告页
+GetPage(name: RoutePath.intimacyAnalyse, page: () => IntimacyAnalysePage()),
+// 亲密度分析上传页
+GetPage(
+name: RoutePath.intimacyAnalyseUpload,
+page: () => IntimacyAnalyseUploadPage(),
+),
 
-  GetPage(name: RoutePath.changeNickname, page: () => ChangeNicknamePage()),
-  GetPage(name: RoutePath.changeGender, page: () => ChangeGenderPage()),
-  GetPage(name: RoutePath.changeBirthday, page: () => ChangeBirthdayPage()),
-  GetPage(name: RoutePath.changeHobbies, page: () => ChangeHobbiesPage()),
-  GetPage(name: RoutePath.changeCharacters, page: () => ChangeCharacterPage()),
+GetPage(name: RoutePath.changeNickname, page: () => ChangeNicknamePage()),
+GetPage(name: RoutePath.changeGender, page: () => ChangeGenderPage()),
+GetPage(name: RoutePath.changeBirthday, page: () => ChangeBirthdayPage()),
+GetPage(name: RoutePath.changeHobbies, page: () => ChangeHobbiesPage()),
+GetPage(name: RoutePath.changeCharacters, page: () => ChangeCharacterPage()),
 
-  // 图片预览页
-  GetPage(name: RoutePath.imageViewer, page: () => ImageViewerPage()),
+// 图片预览页
+GetPage(name: RoutePath.imageViewer, page: () => ImageViewerPage()),
 
-  GetPage(name: RoutePath.userInfo, page: () => UserInfoPage()),
+GetPage(name: RoutePath.userInfo, page: () => UserInfoPage()),
+GetPage(name: RoutePath.newUser, page: () => NewUserPage()),
+GetPage(name: RoutePath.newUserResult, page:()=> NewUserResultPage()),
 ];

+ 40 - 0
lib/utils/age_zodiac_sign_util.dart

@@ -128,6 +128,18 @@ class AgeZodiacSignUtil {
 
     return null;
   }
+
+  static Zodiac getZodiacWithGenderLabel(
+      String birthStr,
+      int genderCode, {
+        String format = 'yyyy-MM-dd',
+      }) {
+    final zodiac = getZodiacSignFromString(birthStr, format: format);
+    final gender = genderCode == 1 ? '男' : genderCode == 2 ? '女' : '';
+    final baseName = zodiac.name.replaceAll('座', '');
+    return Zodiac(name: '$baseName$gender', image: zodiac.image);
+  }
+
 }
 
 class Zodiac {
@@ -136,3 +148,31 @@ class Zodiac {
 
   const Zodiac({required this.name, required this.image});
 }
+
+class DateComponents {
+  final String yearPart;
+  final String monthPart;
+  final String dayPart;
+
+  const DateComponents({
+    required this.yearPart,
+    required this.monthPart,
+    required this.dayPart,
+  });
+
+  /// 从 DateTime 生成格式化数据
+  factory DateComponents.fromDateTime(DateTime date) {
+    return DateComponents(
+      yearPart: DateFormat('yyyy').format(date),
+      monthPart: DateFormat('MM').format(date),
+      dayPart: DateFormat('dd').format(date),
+    );
+  }
+
+  static String getZodiacLabel(Zodiac zodiac, String gender) {
+    final name = zodiac.name.replaceAll('座', '');
+    return '$name$gender'; // gender: '男' 或 '女'
+  }
+
+  String toFormattedString() => '$yearPart年$monthPart月$dayPart日';
+}

+ 48 - 12
lib/utils/intimacy_util.dart

@@ -1,20 +1,56 @@
 class IntimacyUtil {
-  static final List<Map<String, dynamic>> intimacyLevels = [
-    {"max": 20.0, "min": 0.0, "name": "初相识"},
-    {"max": 40.0, "min": 21.0, "name": "追爱期"},
-    {"max": 60.0, "min": 41.0, "name": "暧昧期"},
-    {"max": 80.0, "min": 61.0, "name": "恋人"},
-    {"max": 90.0, "min": 81.0, "name": "伴侣"},
-    {"max": 100.0, "min": 91.0, "name": "老夫老妻"},
+  IntimacyUtil._();
+
+  static final List<IntimacyLevel> intimacyLevels = [
+    IntimacyLevel(max: 20, min: 0, name: "初相识", median: 10),
+    IntimacyLevel(max: 40, min: 21, name: "追爱期", median: 30),
+    IntimacyLevel(max: 60, min: 41, name: "暧昧期", median: 50),
+    IntimacyLevel(max: 80, min: 61, name: "恋人", median: 70),
+    IntimacyLevel(max: 90, min: 81, name: "伴侣", median: 85),
+    IntimacyLevel(max: 100, min: 91, name: "老夫老妻", median: 95),
   ];
 
-  /// 传入 intimacy 值,返回对应的名称
+  /// 获取亲密程度名称
   static String getIntimacyName(int intimacy) {
-    for (var level in intimacyLevels) {
-      if (intimacy >= level["min"] && intimacy <= level["max"]) {
-        return level["name"];
+    for (final level in intimacyLevels) {
+      if (intimacy >= level.min && intimacy <= level.max) {
+        return level.name;
+      }
+    }
+    return "未知";
+  }
+
+  /// 获取对应的亲密程度对象
+  static IntimacyLevel? getIntimacyLevel(int intimacy) {
+    for (final level in intimacyLevels) {
+      if (intimacy >= level.min && intimacy <= level.max) {
+        return level;
       }
     }
-    return "未知"; // 处理异常情况
+    return null;
+  }
+
+  static int getMedianByIndex(int index) {
+    if (index >= 0 && index < intimacyLevels.length) {
+      return intimacyLevels[index].median;
+    }
+    return 0;
   }
 }
+
+class IntimacyLevel {
+  final int max;
+  final int min;
+  final String name;
+  final int median;
+
+  const IntimacyLevel({
+    required this.max,
+    required this.min,
+    required this.name,
+    required this.median,
+  });
+
+  @override
+  String toString() => 'IntimacyLevel($name: $min-$max)';
+}

+ 8 - 0
lib/utils/styles.dart

@@ -43,6 +43,14 @@ class Styles {
     );
   }
 
+  static TextStyle getTextStyleBlack128W400(double? sp) {
+    return TextStyle(
+      color: Colors.black.withAlpha(128),
+      fontSize: sp,
+      fontWeight: FontWeight.w400,
+    );
+  }
+
 
 
   static TextStyle getTextStyleBlack153W400(double? sp) {

+ 22 - 9
lib/widget/auto_scroll_list_view.dart

@@ -1,6 +1,7 @@
 import 'dart:async';
 
 import 'package:flutter/cupertino.dart';
+import 'package:flutter/rendering.dart';
 
 class AutoScrollListView extends StatefulWidget {
   final NullableIndexedWidgetBuilder itemBuilder;
@@ -45,15 +46,22 @@ class _AutoScrollListViewState extends State<AutoScrollListView> {
     _timer?.cancel();
   }
 
+
   void _startAutoScroll() {
     _timer = Timer.periodic(Duration(milliseconds: 50), (timer) {
-      if (_isUserScrolling) {
-        scrollController.animateTo(
-          scrollController.position.pixels + 1,
-          duration: Duration(milliseconds: 50),
-          curve: Curves.linear,
-        );
+      if (!_isUserScrolling || !scrollController.hasClients) return;
+
+      final maxScrollExtent = scrollController.position.maxScrollExtent;
+      final current = scrollController.position.pixels;
+
+      double next = current + 1;
+
+      if (next >= maxScrollExtent) {
+
+        next = 0;
       }
+
+      scrollController.jumpTo(next);
     });
   }
 
@@ -72,9 +80,14 @@ class _AutoScrollListViewState extends State<AutoScrollListView> {
 
   @override
   Widget build(BuildContext context) {
-    return GestureDetector(
-      onPanDown: (_) => _onUserScroll(),
-      onPanUpdate: (_) => _onUserScroll(),
+    return NotificationListener<UserScrollNotification>(
+      onNotification: (notification) {
+        if (notification.direction != ScrollDirection.idle) {
+          _onUserScroll(); // 用户滑动时暂停自动滚动
+        }
+        return false; // 不阻止子组件的事件传递
+      },
+
       child: ListView.builder(
         padding: widget.padding,
         scrollDirection: widget.scrollDirection,

+ 5 - 2
lib/widget/birthday_date_picker.dart

@@ -7,6 +7,8 @@ class BirthdayDatePicker extends StatefulWidget {
   final DateTime minimumDate;
   final DateTime maximumDate;
   final void Function(DateTime) onDateChanged;
+  // 中间背景色
+  final Color backgroundColor;
 
   const BirthdayDatePicker({
     super.key,
@@ -14,6 +16,7 @@ class BirthdayDatePicker extends StatefulWidget {
     required this.minimumDate,
     required this.maximumDate,
     required this.onDateChanged,
+    this.backgroundColor = const Color.fromRGBO(255, 255, 255, 0.6),
   });
 
   @override
@@ -47,7 +50,7 @@ class _BirthdayDatePickerState extends State<BirthdayDatePicker> {
             child: Container(
               height: 36.h,
               decoration: BoxDecoration(
-                color: Colors.white.withValues(alpha: 0.6), // Background color
+                color: widget.backgroundColor, // Background color
                 borderRadius: BorderRadius.circular(14.r),
               ),
             ),
@@ -122,7 +125,7 @@ class _BirthdayDatePickerState extends State<BirthdayDatePicker> {
                 itemBuilder(index),
                 style: TextStyle(
                   fontSize: 16.sp,
-                  fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
+
                   color: isSelected ? Colors.black : Colors.black,
                 ),
               ),

+ 52 - 0
lib/widget/step_progress_bar.dart

@@ -0,0 +1,52 @@
+
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+// 新人流程步骤进度条
+class StepProgressBar extends StatelessWidget {
+  final int currentStep;
+  final int totalSteps;
+
+
+  final Color backgroundColor;
+  final Color activeColor;
+  final double height;
+
+  const StepProgressBar({
+    super.key,
+    required this.currentStep,
+    required this.totalSteps,
+    this.backgroundColor = const Color(0x198A54FD),
+    this.activeColor = const Color(0xFFA084FF),
+    this.height = 8.0,
+  }) : assert(totalSteps > 0, 'totalSteps must be greater than 0');
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      height: height,
+      padding: EdgeInsets.symmetric(horizontal: 2.w, vertical: 2.w),
+      decoration: ShapeDecoration(
+        color: backgroundColor,
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(5.r),
+        ),
+      ),
+      child: Row(
+        children: List.generate(totalSteps, (index) {
+          final isStepCompleted = index < currentStep;
+          return Expanded(
+            child: Container(
+              // margin: EdgeInsets.symmetric(horizontal: 1.w), //
+              decoration: ShapeDecoration(
+                color: isStepCompleted ? activeColor : Colors.transparent,
+                shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(3.r),
+                ),
+              ),
+            ),
+          );
+        }),
+      ),
+    );
+  }
+}