Переглянути джерело

[feat]增加个人档案编辑的ui,修改键盘插件把获取键盘的id改成获取整个键盘的信息

云天逵 8 місяців тому
батько
коміт
edd026f662

BIN
assets/images/bg_profile_edit_intimacy.webp


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

@@ -199,4 +199,7 @@
     <string name="discount_dialog_character">百种人设</string>
     <string name="discount_dialog_social">扩大社交</string>
 
+    <string name="profile_save">完成</string>
+    <string name="profile_edit_save">保存</string>
+
 </resources>

+ 7 - 0
lib/data/api/atmob_api.dart

@@ -12,6 +12,7 @@ import 'package:keyboard/data/api/request/complaint_submit_request.dart';
 import 'package:keyboard/data/api/request/config_request.dart';
 import 'package:keyboard/data/api/request/keyboard_character_list_request.dart';
 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_list_request.dart';
 import 'package:keyboard/data/api/request/keyboard_update_request.dart';
 import 'package:keyboard/data/api/request/login_request.dart';
@@ -166,11 +167,17 @@ abstract class AtmobApi {
   @POST("/project/keyboard/v1/keyboard/update")
   Future<BaseResponse> keyboardUpdate(@Body() KeyboardUpdateRequest request);
 
+  // 选择键盘
+  @POST("/project/keyboard/v1/keyboard/choose")
+  Future<BaseResponse>keyboardChoose(
+    @Body() KeyboardChooseRequest request,
+  );
   // 获取开场白列表
   @POST("/project/keyboard/v1/keyboard/prologue/list")
   Future<BaseResponse<KeyboardPrologueListResponse>> getPrologueList(
       @Body() AppBaseRequest request,
       );
+
   //获取配置信息
   @POST("/project/keyboard/v1/confs")
   Future<BaseResponse<ConfigResponse>> confs(@Body() ConfigRequest request);

+ 33 - 0
lib/data/api/atmob_api.g.dart

@@ -735,6 +735,39 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
+  Future<BaseResponse<dynamic>> keyboardChoose(
+    KeyboardChooseRequest 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<dynamic>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/keyboard/choose',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<dynamic> _value;
+    try {
+      _value = BaseResponse<dynamic>.fromJson(
+        _result.data!,
+        (json) => json as dynamic,
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
   Future<BaseResponse<KeyboardPrologueListResponse>> getPrologueList(
     AppBaseRequest request,
   ) async {

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

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

+ 70 - 0
lib/data/api/request/keyboard_choose_request.g.dart

@@ -0,0 +1,70 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'keyboard_choose_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+KeyboardChooseRequest _$KeyboardChooseRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    KeyboardChooseRequest(keyboardId: json['keyboardId'] 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> _$KeyboardChooseRequestToJson(
+  KeyboardChooseRequest 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,
+  'keyboardId': instance.keyboardId,
+};

+ 11 - 0
lib/data/repository/keyboard_repository.dart

@@ -10,6 +10,7 @@ import '../../di/get_it.dart';
 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_update_request.dart';
 import '../api/response/keyboard_character_list_response.dart';
 import '../api/response/keyboard_list_response.dart';
@@ -92,6 +93,16 @@ class KeyboardRepository {
         )
         .then(HttpHandler.handle(true));
   }
+  // 选择键盘
+  Future<void> keyboardChoose({
+    required String keyboardId,
+  }) {
+    return atmobApi
+        .keyboardChoose(
+      KeyboardChooseRequest(keyboardId: keyboardId),
+        )
+        .then(HttpHandler.handle(true));
+  }
 
   // 获取开场白列表
   Future<KeyboardPrologueListResponse> getPrologueList() {

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

@@ -40,6 +40,7 @@ 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/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;
 import '../module/store/store_controller.dart' as _i344;
@@ -160,6 +161,9 @@ extension GetItInjectableX on _i174.GetIt {
     gh.factory<_i15.CharacterCustomController>(
       () => _i15.CharacterCustomController(gh<_i50.ConfigRepository>()),
     );
+    gh.factory<_i344.ProfileEditController>(
+      () => _i344.ProfileEditController(gh<_i50.ConfigRepository>()),
+    );
     gh.factory<_i888.CharacterController>(
       () => _i888.CharacterController(
         gh<_i421.CharactersRepository>(),

+ 16 - 11
lib/module/feedback/feedback_page.dart

@@ -10,6 +10,7 @@ import 'package:keyboard/widget/commonAppBar.dart';
 import '../../resource/assets.gen.dart';
 import '../../resource/string.gen.dart';
 import '../../router/app_pages.dart';
+import '../../utils/styles.dart';
 
 class FeedbackPage extends BasePage<FeedbackController> {
   const FeedbackPage({super.key});
@@ -149,22 +150,26 @@ class FeedbackPage extends BasePage<FeedbackController> {
             ),
 
             Spacer(),
-            ElevatedButton(
-              onPressed: () {
+            GestureDetector(
+              onTap: () {
                 controller.submitClick();
               },
-              style: ElevatedButton.styleFrom(
-                backgroundColor: Colors.purple,
-                padding: EdgeInsets.symmetric(vertical: 16.r),
-                shape: RoundedRectangleBorder(
-                  borderRadius: BorderRadius.circular(100.r),
+              child: Container(
+                height: 48.h,
+                alignment: Alignment.center,
+                decoration:
+                Styles.getActivateButtonDecoration(
+                  31.r,
+                ),
+                child: Text(
+                  StringName.feedbackSubmit,
+                  style: Styles.getTextStyleWhiteW500(
+                    16.sp,
+                  ),
                 ),
-              ),
-              child: Text(
-                StringName.feedbackSubmit,
-                style: TextStyle(fontSize: 16.r, color: Colors.white),
               ),
             ),
+
             SizedBox(height: 16.h),
           ],
         ),

+ 4 - 4
lib/module/mine/mine_controller.dart

@@ -41,10 +41,10 @@ class MineController extends BaseController {
   }
 
   clickVip() {
-    // debugPrint('clickVip');
-    // StorePage.start();
-    KeyboardAndroidPlatform.getInstance().enableFloatingWindow(true);
-    KeyboardAndroidPlatform.getInstance().openInputMethodSettings();
+    debugPrint('clickVip');
+    StorePage.start();
+    // KeyboardAndroidPlatform.getInstance().enableFloatingWindow(true);
+    // KeyboardAndroidPlatform.getInstance().openInputMethodSettings();
   }
 
   clickOnlineCustomerService() {

+ 89 - 0
lib/module/profile/edit/profile_edit_controller.dart

@@ -0,0 +1,89 @@
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:keyboard/data/bean/keyboard_info.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+import 'package:get/get.dart';
+
+import '../../../data/bean/custom_config_info.dart';
+import '../../../data/repository/config_repository.dart';
+
+@injectable
+class ProfileEditController extends BaseController {
+  final tag = "ProfileEditController";
+
+  final ConfigRepository configRepository;
+
+  final RxInt _gender = 1.obs;
+
+  final RxString _avatarUrl = "".obs;
+
+  String get avatarUrl => _avatarUrl.value;
+
+  CustomConfigInfo? get currentCharacterCustomConfig =>
+      configRepository.characterCustomConfig;
+
+  final Rx<KeyboardInfo> _currentCustomKeyboardInfo = KeyboardInfo().obs;
+
+  KeyboardInfo get currentCustomKeyboardInfo =>
+      _currentCustomKeyboardInfo.value;
+
+  // 当前自定义键盘亲密度
+  final RxInt _currentCustomIntimacy = 0.obs;
+
+  int get currentCustomIntimacy => _currentCustomIntimacy.value;
+
+  // 当前定制亲密度是否有变化
+  final RxBool _customIntimacyChanged = false.obs;
+
+  bool get customIntimacyChanged => _customIntimacyChanged.value;
+
+  final List<String> _boyAvatars = [];
+
+  final List<String> _girlAvatars = [];
+
+  ProfileEditController(this.configRepository);
+
+  @override
+  void onInit() {
+    super.onInit();
+    initData();
+  }
+
+  void initData() {
+    AtmobLog.d(tag, "initData");
+    _boyAvatars.addAll(currentCharacterCustomConfig?.boyAvatars ?? []);
+    _girlAvatars.addAll(currentCharacterCustomConfig?.girlAvatars ?? []);
+    if (_gender.value == 1) {
+      _avatarUrl.value = _boyAvatars[0];
+    } else {
+      _avatarUrl.value = _girlAvatars[0];
+    }
+  }
+
+  clickSaveButton() {
+    AtmobLog.d(tag, 'clickSaveButton');
+  }
+
+  void nextAvatar() {
+    AtmobLog.d(tag, "nextAvatar");
+
+    if (_gender.value == 1) {
+      int currentIndex = _boyAvatars.indexOf(_avatarUrl.value);
+      _avatarUrl.value = _boyAvatars[(currentIndex + 1) % _boyAvatars.length];
+    } else {
+      int currentIndex = _girlAvatars.indexOf(_avatarUrl.value);
+      _avatarUrl.value = _girlAvatars[(currentIndex + 1) % _girlAvatars.length];
+    }
+  }
+  // 更新亲密度
+  void updateIntimacy(int intimacy) {
+
+      _currentCustomIntimacy.value = intimacy;
+
+  }
+
+  clickBack() {
+    AtmobLog.d(tag, 'clickBackButton');
+    Get.back();
+  }
+}

+ 375 - 0
lib/module/profile/edit/profile_edit_page.dart

@@ -0,0 +1,375 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/module/profile/edit/profile_edit_controller.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+import '../../../resource/assets.gen.dart';
+import '../../../resource/string.gen.dart';
+import '../../../router/app_pages.dart';
+import '../../../utils/styles.dart';
+import '../../../widget/gradient_rect_slider_track_shape.dart';
+
+class ProfileEditPage extends BasePage<ProfileEditController> {
+  const ProfileEditPage({super.key});
+
+  static start() {
+    Get.toNamed(RoutePath.profileEdit);
+  }
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  backgroundColor() {
+    return Color(0xFFF6F5FA);
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        Container(
+          child: Assets.images.bgCharacterCustomDetail.image(
+            width: double.infinity,
+            fit: BoxFit.fill,
+          ),
+        ),
+        SafeArea(
+          child: Container(
+            color: Colors.transparent,
+            alignment: Alignment.topCenter,
+            child: Stack(
+              children: [
+                Column(
+                  children: [
+                    _buildTitle(),
+                    SizedBox(height: 42.h),
+                    Expanded(
+                      child: Container(
+                        decoration: ShapeDecoration(
+                          color: Color(0xFFF6F5FA),
+                          shape: RoundedRectangleBorder(
+                            borderRadius: BorderRadius.only(
+                              topLeft: Radius.circular(20.r),
+                              topRight: Radius.circular(20.r),
+                            ),
+                          ),
+                        ),
+                        child: SingleChildScrollView(
+                          child: Column(
+                            children: [
+                              _buildNameCard(),
+                              SizedBox(height: 20.h),
+                              _buildIntimacySlider(),
+                              SizedBox(height: 10.h),
+                              _buildGenderCard(),
+
+                              SizedBox(height: 10.h),
+                              _buildBirthdayCard(),
+                              SizedBox(height: 10.h),
+                            ],
+                          ),
+                        ),
+                      ),
+                    ),
+                    Container(
+                      color: Color(0xFFF6F5FA),
+                      child: _buildSaveButton(),
+                    ),
+                  ],
+                ),
+                Positioned(left: 16.w, top: 60.h, child: _buildAvatar()),
+                Positioned(left: 68.w, top: 112.h, child: _buildAvatarSwitch()),
+              ],
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+
+  _buildTitle() {
+    return Container(
+      alignment: Alignment.centerLeft,
+      padding: EdgeInsets.only(top: 12.h, left: 16.w),
+      child: GestureDetector(
+        onTap: controller.clickBack,
+        child: Assets.images.iconMineBackArrow.image(width: 24.w, height: 24.w),
+      ),
+    );
+  }
+
+  _buildNameCard() {
+    return Container(
+      padding: EdgeInsets.only(left: 104.w, top: 14.h),
+      child: Row(
+        crossAxisAlignment: CrossAxisAlignment.end,
+        children: [
+          Text(
+            controller.currentCustomKeyboardInfo.name ?? '请输入昵称',
+            textAlign: TextAlign.center,
+            style: TextStyle(
+              color: Colors.black.withAlpha(204),
+              fontSize: 18.sp,
+              fontWeight: FontWeight.w500,
+            ),
+          ),
+          Container(
+            child: Assets.images.iconCharacterCustomDetailEdit.image(
+              width: 20.r,
+              height: 20.r,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  _buildAvatar() {
+    return GestureDetector(
+      onTap: controller.nextAvatar,
+      child: Container(
+        width: 72.r,
+        height: 72.r,
+        decoration: ShapeDecoration(
+          shape: OvalBorder(side: BorderSide(width: 2, color: Colors.white)),
+        ),
+        child:
+            controller.avatarUrl.isNotEmpty
+                ? CachedNetworkImage(
+                  imageUrl: controller.avatarUrl,
+                  width: 72.r,
+                  height: 72.r,
+                )
+                : SizedBox(),
+      ),
+    );
+  }
+
+  _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 _buildGenderCard() {
+    return _buildListItem(
+      onTap: () {
+        debugPrint('点击了性别');
+      },
+      firstWidget: Text('性别', style: Styles.getTextStyleBlack204W400(14.sp)),
+      bottomWidget: Row(
+        children: [
+          Assets.images.iconCharacterCustomDetailMale.image(
+            width: 24.w,
+            height: 24.w,
+          ),
+          SizedBox(width: 6.w),
+          Text('男', style: Styles.getTextStyleBlack204W400(14.sp)),
+          Spacer(),
+          Assets.images.iconArrowRight.image(width: 24.w, height: 24.w),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildBirthdayCard() {
+    return _buildListItem(
+      onTap: () {
+        debugPrint('点击了生日');
+      },
+      firstWidget: Text('出生日期', style: Styles.getTextStyleBlack204W400(14.sp)),
+      bottomWidget: Row(
+        children: [
+          Text('1998-12-16', style: Styles.getTextStyleBlack204W400(14.sp)),
+          SizedBox(width: 12.w),
+          Text('22岁', style: Styles.getTextStyleBlack204W400(14.sp)),
+          Spacer(),
+          Assets.images.iconArrowRight.image(width: 24.w, height: 24.w),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildSaveButton() {
+    return GestureDetector(
+      onTap: () {
+        controller.clickSaveButton();
+      },
+      child: Container(
+        width: double.infinity,
+        margin: EdgeInsets.only(left: 16.w, right: 16.w, bottom: 16.h),
+        height: 48.h,
+        alignment: Alignment.center,
+        decoration: ShapeDecoration(
+          color: const Color(0xFF7D46FC),
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(50.r),
+          ),
+        ),
+        child: Text(
+          StringName.profileEditSave,
+          textAlign: TextAlign.center,
+          style: Styles.getTextStyleWhiteW500(16.sp),
+        ),
+      ),
+    );
+  }
+
+  // 列表项
+  Widget _buildListItem({
+    required Widget firstWidget,
+    required Widget bottomWidget,
+    VoidCallback? onTap,
+  }) {
+    return Container(
+      padding: EdgeInsets.only(
+        left: 12.w,
+        right: 12.w,
+        top: 14.h,
+        bottom: 14.h,
+      ),
+      margin: EdgeInsets.only(left: 16.w, right: 16.w),
+      width: double.infinity,
+      decoration: ShapeDecoration(
+        color: Colors.white,
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(12.r),
+        ),
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          firstWidget,
+          _buildDivider(),
+          GestureDetector(
+            behavior: HitTestBehavior.opaque,
+            onTap: onTap,
+            child: bottomWidget,
+          ),
+        ],
+      ),
+    );
+  }
+
+  // 下划线
+  Widget _buildDivider() {
+    return Container(
+      margin: EdgeInsets.only(top: 8.h, bottom: 8.h),
+      width: 304.w,
+      decoration: ShapeDecoration(
+        shape: RoundedRectangleBorder(
+          side: BorderSide(
+            width: 1.r,
+            strokeAlign: BorderSide.strokeAlignCenter,
+            color: const Color(0xFFF5F4F9),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildIntimacySlider() {
+    return // 亲密度模块
+    Container(
+      margin: EdgeInsets.only(left: 16.w, top: 24.h, right: 16.w),
+      padding: EdgeInsets.only(
+        left: 16.w,
+        top: 23.h,
+        right: 16.w,
+        bottom: 26.h,
+      ),
+      decoration: BoxDecoration(
+        image: DecorationImage(
+          image: Assets.images.bgProfileEditIntimacy.provider(),
+          fit: BoxFit.fill,
+        ),
+        borderRadius: BorderRadius.circular(10.r),
+      ),
+      child: Column(
+        children: [
+          // 亲密度
+          Row(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              Assets.images.iconKeyboardManageFavorite.image(
+                width: 20.w,
+                height: 20.w,
+              ),
+              Assets.images.iconKeyboardManageIntimacyText.image(
+                width: 48.w,
+                height: 19.h,
+              ),
+              const Spacer(),
+              Container(
+                alignment: Alignment.center,
+                width: 81.w,
+                height: 28.h,
+                decoration: ShapeDecoration(
+                  color: const Color(0xFFE1E0E7),
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(16.r),
+                  ),
+                ),
+                child: Obx(() {
+                  return Text(
+                    '${StringName.intimacy}${controller.currentCustomIntimacy}%',
+                    textAlign: TextAlign.right,
+                    style: TextStyle(
+                      color: Colors.black.withAlpha(204),
+                      fontSize: 12.sp,
+                      fontWeight: FontWeight.w400,
+                    ),
+                  );
+                }),
+              ),
+            ],
+          ),
+          SizedBox(height: 19.h),
+          Builder(
+            builder: (context) {
+              return SliderTheme(
+                data: SliderTheme.of(context).copyWith(
+                  trackShape: const GradientRectSliderTrackShape(),
+                  trackHeight: 8.h,
+                  thumbColor: Colors.white,
+                  thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7.r),
+                  overlayShape: const RoundSliderOverlayShape(
+                    overlayRadius: 16,
+                  ),
+                ),
+                child: Obx(() {
+                  return Slider(
+                    value: controller.currentCustomIntimacy.toDouble(),
+
+                    divisions: 100,
+                    min: 0,
+                    max: 100,
+                    onChanged: (value) {
+                      controller.updateIntimacy(value.toInt());
+                    },
+                  );
+                }),
+              );
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 29 - 19
lib/module/profile/profile_controller.dart

@@ -2,11 +2,15 @@ import 'package:get/get_rx/src/rx_types/rx_types.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/data/repository/account_repository.dart';
+import 'package:keyboard/module/profile/edit/profile_edit_page.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 import 'package:get/get.dart';
 import '../../data/api/response/user_info_response.dart';
 import '../../data/bean/keyboard_info.dart';
 import '../../data/repository/keyboard_repository.dart';
+import '../../utils/error_handler.dart';
+import '../../utils/http_handler.dart';
+import '../../utils/toast_util.dart';
 import '../keyboard_manage/keyboard_manage_controller.dart';
 
 @injectable
@@ -25,20 +29,9 @@ class ProfileController extends BaseController {
   KeyboardInfo get currentCustomKeyboardInfo =>
       _currentCustomKeyboardInfo.value;
 
-
-
-
   final RxList<KeyboardInfo> _customKeyboardInfoList = RxList();
-  RxList<KeyboardInfo> get keyboardInfoList =>  _customKeyboardInfoList;
-  // 当前自定义键盘亲密度
-  final RxInt _currentCustomIntimacy = 0.obs;
-
-  int get currentCustomIntimacy => _currentCustomIntimacy.value;
-
-  // 当前定制亲密度是否有变化
-  final RxBool _customIntimacyChanged = false.obs;
 
-  bool get customIntimacyChanged => _customIntimacyChanged.value;
+  RxList<KeyboardInfo> get keyboardInfoList => _customKeyboardInfoList;
 
   ProfileController(this.keyboardRepository, this.accountRepository) {
     AtmobLog.d(tag, '....init');
@@ -70,13 +63,6 @@ class ProfileController extends BaseController {
           (element) => element.isChoose == true,
           orElse: () => _customKeyboardInfoList.first,
         );
-        _currentCustomIntimacy.value =
-            _currentCustomKeyboardInfo.value.intimacy ?? 0;
-        _currentCustomIntimacy.listen((intimacy) {
-          _customIntimacyChanged.value =
-              _currentCustomKeyboardInfo.value.intimacy != intimacy;
-          AtmobLog.d(tag, 'intimacyChanged: $_customIntimacyChanged');
-        });
       }
     });
   }
@@ -93,5 +79,29 @@ class ProfileController extends BaseController {
 
   clickAddButton() {
     AtmobLog.d(tag, "clickAddButton");
+    ProfileEditPage.start(
+
+    );
+  }
+
+  clickSaveButton() {
+    AtmobLog.d(tag, "clickSaveButton");
+    final keyboardInfo = _currentCustomKeyboardInfo.value;
+    if(_currentCustomKeyboardInfo.value.isChoose==true){
+      ToastUtil.show("当前键盘已选择");
+      return;
+    }
+    if (keyboardInfo.id?.isNotEmpty == true) {
+      try {
+        keyboardRepository.keyboardChoose(keyboardId: keyboardInfo.id!);
+        ToastUtil.show("当前键盘已选择");
+      } catch (error) {
+        if (error is ServerErrorException) {
+          ToastUtil.show(error.message);
+        } else {
+          ErrorHandler.toastError(error);
+        }
+      }
+    }
   }
 }

+ 20 - 1
lib/module/profile/profile_page.dart

@@ -8,6 +8,7 @@ import 'package:keyboard/module/profile/profile_controller.dart';
 import 'package:keyboard/utils/intimacy_util.dart';
 
 import '../../resource/assets.gen.dart';
+import '../../resource/string.gen.dart';
 import '../../router/app_pages.dart';
 import '../../utils/styles.dart';
 
@@ -65,7 +66,25 @@ class ProfilePage extends BasePage<ProfileController> {
             ],
           ),
         ),
-
+        Positioned(
+          bottom: 20.h,
+          left: 16.w,
+          right: 16.w,
+          child: GestureDetector(
+            onTap: () {
+              controller.clickSaveButton();
+            },
+            child: Container(
+              height: 48.h,
+              alignment: Alignment.center,
+              decoration: Styles.getActivateButtonDecoration(31.r),
+              child: Text(
+                StringName.profileSave,
+                style: Styles.getTextStyleWhiteW500(16.sp),
+              ),
+            ),
+          ),
+        ),
         // 背景图片
         IgnorePointer(child: Assets.images.bgMine.image(width: 360.w)),
       ],

+ 19 - 10
lib/plugins/keyboard_method_handler.dart

@@ -4,6 +4,8 @@ import 'package:keyboard/data/bean/member_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 '../data/api/response/keyboard_list_response.dart';
+import '../data/bean/keyboard_info.dart';
 import '../utils/mmkv_util.dart';
 
 class KeyboardMethodHandler {
@@ -15,7 +17,9 @@ class KeyboardMethodHandler {
   static const String keyboardSelect = 'keyboard_select';
 
   bool get isLogin => accountRepository.isLogin.value;
+
   bool get isMember => accountRepository.isMember.value;
+  late KeyboardListResponse keyboardListResponse;
 
   KeyboardMethodHandler(
     this.keyboardRepository,
@@ -31,8 +35,8 @@ class KeyboardMethodHandler {
         return _handleSelectedKeyboard(call);
       case 'getCharacterList':
         return _handleGetCharacterList(call);
-      case 'getCurrentKeyboardId':
-        return _handleGetCurrentKeyboardId(call);
+      case 'getCurrentKeyboardInfo':
+        return _handleGetCurrentKeyboardInfo(call);
       case 'getPrologueList':
         return _handleGetPrologueList(call);
       case 'isLogin':
@@ -40,7 +44,6 @@ class KeyboardMethodHandler {
       case 'isMember':
         return isMember;
 
-
       default:
         throw MissingPluginException('Not implemented: ${call.method}');
     }
@@ -59,21 +62,27 @@ class KeyboardMethodHandler {
         }
       }
     }
+    keyboardListResponse = keyboardList;
     return jsonEncode(keyboardList.toJson());
   }
 
   Future<String> _handleSelectedKeyboard(MethodCall call) async {
     final String keyboardId = call.arguments['keyboardId'];
-    KVUtil.putString(keyboardSelect, keyboardId);
+
+    if ( keyboardListResponse.keyboardInfos.isEmpty) {
+      return jsonEncode({"error": "Keyboard list not initialized"});
+    }
+    final selectedKeyboard = keyboardListResponse.keyboardInfos.firstWhere(
+      (element) => element.id == keyboardId,
+      orElse: () => KeyboardInfo(),
+    );
+    KVUtil.putString(keyboardSelect, jsonEncode(selectedKeyboard.toJson()));
     return "{}";
   }
 
-  Future<String> _handleGetCurrentKeyboardId(MethodCall call) async {
-    String? keyboardId = KVUtil.getString(keyboardSelect, null);
-    if (keyboardId == null) {
-      return "{}";
-    }
-    return jsonEncode({"keyboardId": keyboardId});
+  Future<String> _handleGetCurrentKeyboardInfo(MethodCall call) async {
+    final String? keyboardJsonStr = KVUtil.getString(keyboardSelect, null);
+    return keyboardJsonStr ?? "{}";
   }
 
   Future<String> _handleGetCharacterList(MethodCall call) async {

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

@@ -112,6 +112,10 @@ class $AssetsImagesGen {
   AssetGenImage get bgMineVipCard =>
       const AssetGenImage('assets/images/bg_mine_vip_card.webp');
 
+  /// File path: assets/images/bg_profile_edit_intimacy.webp
+  AssetGenImage get bgProfileEditIntimacy =>
+      const AssetGenImage('assets/images/bg_profile_edit_intimacy.webp');
+
   /// File path: assets/images/bg_profile_love.webp
   AssetGenImage get bgProfileLove =>
       const AssetGenImage('assets/images/bg_profile_love.webp');
@@ -560,6 +564,7 @@ class $AssetsImagesGen {
     bgKeyboardManageIntimacy,
     bgMine,
     bgMineVipCard,
+    bgProfileEditIntimacy,
     bgProfileLove,
     bgProfileSelected,
     bgStoreSelectedItem,

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

@@ -141,6 +141,8 @@ class StringName {
   static final String discountDialogTutorial = 'discount_dialog_tutorial'.tr; // 恋爱教程
   static final String discountDialogCharacter = 'discount_dialog_character'.tr; // 百种人设
   static final String discountDialogSocial = 'discount_dialog_social'.tr; // 扩大社交
+  static final String profileSave = 'profile_save'.tr; // 完成
+  static final String profileEditSave = 'profile_edit_save'.tr; // 保存
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -285,6 +287,8 @@ class StringMultiSource {
       'discount_dialog_tutorial': '恋爱教程',
       'discount_dialog_character': '百种人设',
       'discount_dialog_social': '扩大社交',
+      'profile_save': '完成',
+      'profile_edit_save': '保存',
     },
   };
 }

+ 5 - 0
lib/router/app_pages.dart

@@ -10,6 +10,7 @@ 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/profile/edit/profile_edit_controller.dart';
 import 'package:keyboard/module/profile/profile_controller.dart';
 import 'package:keyboard/module/profile/profile_page.dart';
 import 'package:keyboard/module/store/store_controller.dart';
@@ -27,6 +28,7 @@ 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/profile/edit/profile_edit_page.dart';
 import '../module/store/discount/discount_controller.dart';
 import '../module/store/suprise/goods_surprise_controller.dart';
 
@@ -48,6 +50,7 @@ abstract class RoutePath {
   static const characterCustomList = '/characterCustomList';
   static const store = '/store';
   static const profile = '/profile';
+  static const profileEdit = '/profileEdit';
 }
 
 class AppBinding extends Bindings {
@@ -70,6 +73,7 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<GoodsSurpriseController>());
     lazyPut(() => getIt.get<DiscountController>());
     lazyPut(() => getIt.get<ProfileController>());
+    lazyPut(() => getIt.get<ProfileEditController>());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -95,4 +99,5 @@ final generalPages = [
   ),
   GetPage(name: RoutePath.store, page: () => StorePage()),
   GetPage(name: RoutePath.profile, page: () => ProfilePage()),
+  GetPage(name: RoutePath.profileEdit, page: () => ProfileEditPage()),
 ];

+ 2 - 2
lib/utils/styles.dart

@@ -6,8 +6,8 @@ class Styles {
   static Decoration getActivateButtonDecoration(double radius) {
     return ShapeDecoration(
       gradient: LinearGradient(
-        begin: Alignment.topCenter,
-        end: Alignment.centerRight,
+        begin: Alignment(0.04, 0.21),
+        end: Alignment(0.98, 0.76),
         colors: [const Color(0xFF7D46FC), const Color(0xFFBC87FF)],
       ),