Quellcode durchsuchen

[feat]新增人设页

云天逵 vor 8 Monaten
Ursprung
Commit
399d3eab5c
44 geänderte Dateien mit 1821 neuen und 34 gelöschten Zeilen
  1. BIN
      assets/images/icon_character_arrow_down.webp
  2. BIN
      assets/images/icon_character_arrow_right.webp
  3. BIN
      assets/images/icon_character_customized.webp
  4. BIN
      assets/images/icon_character_group_selected.webp
  5. BIN
      assets/images/icon_character_keyboard.webp
  6. BIN
      assets/images/icon_character_market.webp
  7. BIN
      assets/images/icon_mine_vip.webp
  8. 4 0
      assets/string/base/string.xml
  9. 32 0
      lib/data/api/atmob_api.dart
  10. 164 0
      lib/data/api/atmob_api.g.dart
  11. 29 0
      lib/data/api/request/character_page_request.dart
  12. 78 0
      lib/data/api/request/character_page_request.g.dart
  13. 71 0
      lib/data/api/request/complaint_submit_request.g.dart
  14. 16 0
      lib/data/api/request/config_request.dart
  15. 72 0
      lib/data/api/request/config_request.g.dart
  16. 35 0
      lib/data/api/request/user_info_setting_request.dart
  17. 88 0
      lib/data/api/request/user_info_setting_request.g.dart
  18. 20 0
      lib/data/api/response/character_group_response.g.dart
  19. 19 0
      lib/data/api/response/character_page_response.dart
  20. 21 0
      lib/data/api/response/character_page_response.g.dart
  21. 16 0
      lib/data/api/response/config_response.dart
  22. 17 0
      lib/data/api/response/config_response.g.dart
  23. 18 0
      lib/data/api/response/new_user_get_character_response.dart
  24. 20 0
      lib/data/api/response/new_user_get_character_response.g.dart
  25. 59 0
      lib/data/api/response/user_info_response.dart
  26. 39 0
      lib/data/api/response/user_info_response.g.dart
  27. 2 2
      lib/data/bean/character_group_info.dart
  28. 21 0
      lib/data/bean/character_group_info.g.dart
  29. 51 0
      lib/data/bean/character_info.dart
  30. 31 0
      lib/data/bean/character_info.g.dart
  31. 19 0
      lib/data/bean/config_bean.dart
  32. 13 0
      lib/data/bean/config_bean.g.dart
  33. 19 0
      lib/data/bean/member_info.dart
  34. 18 0
      lib/data/bean/member_info.g.dart
  35. 82 19
      lib/data/repository/account_repository.dart
  36. 33 2
      lib/data/repository/characters_repository.dart
  37. 41 0
      lib/data/repository/config_repository.dart
  38. 8 1
      lib/di/get_it.config.dart
  39. 93 6
      lib/module/character/character_controller.dart
  40. 431 4
      lib/module/character/character_view.dart
  41. 2 0
      lib/module/mine/mine_controller.dart
  42. 30 0
      lib/resource/assets.gen.dart
  43. 106 0
      lib/resource/string.gen.dart
  44. 3 0
      pubspec.yaml

BIN
assets/images/icon_character_arrow_down.webp


BIN
assets/images/icon_character_arrow_right.webp


BIN
assets/images/icon_character_customized.webp


BIN
assets/images/icon_character_group_selected.webp


BIN
assets/images/icon_character_keyboard.webp


BIN
assets/images/icon_character_market.webp


BIN
assets/images/icon_mine_vip.webp


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

@@ -62,4 +62,8 @@
     <string name="service_terms">服务条款</string>
     <string name="child_privacy_policy">儿童隐私保护协议</string>
 
+    <!--    人设页面-->
+    <string name="go_customize_character">去定制人设</string>
+    <string name="go_customize_character_desc">定制你自己的专属人设</string>
+    <string name="my_keyboard">我的键盘</string>
 </resources>

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

@@ -1,10 +1,18 @@
 import 'package:dio/dio.dart';
 import 'package:keyboard/base/base_response.dart';
+import 'package:keyboard/data/api/request/character_page_request.dart';
 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/login_request.dart';
 import 'package:keyboard/data/api/request/send_code_request.dart';
+import 'package:keyboard/data/api/request/user_info_setting_request.dart';
 import 'package:keyboard/data/api/response/character_group_response.dart';
+import 'package:keyboard/data/api/response/character_page_response.dart';
+import 'package:keyboard/data/api/response/config_response.dart';
 import 'package:keyboard/data/api/response/login_response.dart';
+import 'package:keyboard/data/api/response/new_user_get_character_response.dart';
+import 'package:keyboard/data/api/response/user_info_response.dart';
+import 'package:keyboard/data/repository/config_repository.dart';
 import 'package:retrofit/error_logger.dart';
 import 'package:retrofit/http.dart';
 
@@ -34,11 +42,35 @@ abstract class AtmobApi {
   @POST("/project/keyboard/v1/complaint/submit")
   Future<BaseResponse> complaintSubmit(@Body() ComplaintSubmitRequest request);
 
+  // 获取用户信息
+  @POST("/project/keyboard/v1/user/info")
+  Future<BaseResponse<UserInfoResponse>> getUserInfo(
+    @Body() AppBaseRequest request,
+  );
+
+  // 设置用户信息
+  @POST("/project/keyboard/v1/user/info/setting")
+  Future<BaseResponse> setUserInfo(@Body() UserInfoSettingRequest request);
+
+//  获取新人流程人设列表
+  @POST("/project/keyboard/v1/character/newUser/getCharacter")
+  Future<BaseResponse<NewUserGetCharacterResponse>> getNewUserCharactersPage(
+    @Body() AppBaseRequest request,
+  );
+
   // 获取人设主题
   @POST("/project/keyboard/v1/character/group")
   Future<BaseResponse<CharacterGroupResponse>> getCharactersGroup(
     @Body() AppBaseRequest request,
   );
 
+  // 获取人设列表
+  @POST("/project/keyboard/v1/character/page")
+  Future<BaseResponse<CharacterPageResponse>> getCharactersPage(
+    @Body() CharacterPageRequest request,
+  );
 
+  //获取配置信息
+  @POST("/project/keyboard/v1/confs")
+  Future<BaseResponse<ConfigResponse>> confs(@Body() ConfigRequest request);
 }

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

@@ -146,6 +146,106 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
+  Future<BaseResponse<UserInfoResponse>> getUserInfo(
+    AppBaseRequest 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<UserInfoResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/user/info',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<UserInfoResponse> _value;
+    try {
+      _value = BaseResponse<UserInfoResponse>.fromJson(
+        _result.data!,
+        (json) => UserInfoResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<dynamic>> setUserInfo(
+    UserInfoSettingRequest 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/user/info/setting',
+            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<NewUserGetCharacterResponse>> getNewUserCharactersPage(
+    AppBaseRequest 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<NewUserGetCharacterResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/character/newUser/getCharacter',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<NewUserGetCharacterResponse> _value;
+    try {
+      _value = BaseResponse<NewUserGetCharacterResponse>.fromJson(
+        _result.data!,
+        (json) =>
+            NewUserGetCharacterResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
   Future<BaseResponse<CharacterGroupResponse>> getCharactersGroup(
     AppBaseRequest request,
   ) async {
@@ -178,6 +278,70 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<CharacterPageResponse>> getCharactersPage(
+    CharacterPageRequest 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<CharacterPageResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/character/page',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<CharacterPageResponse> _value;
+    try {
+      _value = BaseResponse<CharacterPageResponse>.fromJson(
+        _result.data!,
+        (json) => CharacterPageResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<ConfigResponse>> confs(ConfigRequest 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<ConfigResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/confs',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<ConfigResponse> _value;
+    try {
+      _value = BaseResponse<ConfigResponse>.fromJson(
+        _result.data!,
+        (json) => ConfigResponse.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 ||

+ 29 - 0
lib/data/api/request/character_page_request.dart

@@ -0,0 +1,29 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'character_page_request.g.dart';
+
+@JsonSerializable()
+class CharacterPageRequest extends AppBaseRequest {
+  @JsonKey(name: "groupId")
+  String groupId;
+
+  @JsonKey(name: "keyboardId")
+  String keyboardId;
+
+  @JsonKey(name: "page")
+  int page;
+  @JsonKey(name: "pageSize")
+  int pageSize;
+
+  CharacterPageRequest({
+    required this.groupId,
+    required this.keyboardId,
+    required this.page,
+    required this.pageSize,
+  });
+
+  @override
+  Map<String, dynamic> toJson() => _$CharacterPageRequestToJson(this);
+}

+ 78 - 0
lib/data/api/request/character_page_request.g.dart

@@ -0,0 +1,78 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_page_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterPageRequest _$CharacterPageRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    CharacterPageRequest(
+        groupId: json['groupId'] as String,
+        keyboardId: json['keyboardId'] as String,
+        page: (json['page'] as num).toInt(),
+        pageSize: (json['pageSize'] as num).toInt(),
+      )
+      ..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> _$CharacterPageRequestToJson(
+  CharacterPageRequest 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,
+  'groupId': instance.groupId,
+  'keyboardId': instance.keyboardId,
+  'page': instance.page,
+  'pageSize': instance.pageSize,
+};

+ 71 - 0
lib/data/api/request/complaint_submit_request.g.dart

@@ -0,0 +1,71 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'complaint_submit_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ComplaintSubmitRequest _$ComplaintSubmitRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    ComplaintSubmitRequest(json['phone'] as String?, json['content'] 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> _$ComplaintSubmitRequestToJson(
+  ComplaintSubmitRequest 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,
+  'phone': instance.phone,
+  'content': instance.content,
+};

+ 16 - 0
lib/data/api/request/config_request.dart

@@ -0,0 +1,16 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'config_request.g.dart';
+
+@JsonSerializable()
+class ConfigRequest extends AppBaseRequest {
+  @JsonKey(name: "confCodes")
+  List<String> confCodes;
+
+  ConfigRequest({required this.confCodes});
+
+  @override
+  Map<String, dynamic> toJson() => _$ConfigRequestToJson(this);
+}

+ 72 - 0
lib/data/api/request/config_request.g.dart

@@ -0,0 +1,72 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'config_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ConfigRequest _$ConfigRequestFromJson(Map<String, dynamic> json) =>
+    ConfigRequest(
+        confCodes:
+            (json['confCodes'] as List<dynamic>)
+                .map((e) => e as String)
+                .toList(),
+      )
+      ..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> _$ConfigRequestToJson(ConfigRequest 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,
+      'confCodes': instance.confCodes,
+    };

+ 35 - 0
lib/data/api/request/user_info_setting_request.dart

@@ -0,0 +1,35 @@
+import 'dart:convert';
+
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'user_info_setting_request.g.dart';
+
+@JsonSerializable()
+class UserInfoSettingRequest extends AppBaseRequest {
+  @JsonKey(name: "name")
+  String? name;
+  @JsonKey(name: "birthday")
+  String? birthday;
+  @JsonKey(name: "gender")
+  int? gender;
+  @JsonKey(name: "imageUrl")
+  String? imageUrl;
+  @JsonKey(name: "hobbies")
+  List<String>? hobbies;
+  @JsonKey(name: "characters")
+  List<String>? characters;
+
+  UserInfoSettingRequest({
+    this.name,
+    this.birthday,
+    this.gender,
+    this.characters,
+    this.hobbies,
+    this.imageUrl,
+  });
+
+  @override
+  Map<String, dynamic> toJson() => _$UserInfoSettingRequestToJson(this);
+}

+ 88 - 0
lib/data/api/request/user_info_setting_request.g.dart

@@ -0,0 +1,88 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'user_info_setting_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+UserInfoSettingRequest _$UserInfoSettingRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    UserInfoSettingRequest(
+        name: json['name'] as String?,
+        birthday: json['birthday'] as String?,
+        gender: (json['gender'] as num?)?.toInt(),
+        characters:
+            (json['characters'] as List<dynamic>?)
+                ?.map((e) => e as String)
+                .toList(),
+        hobbies:
+            (json['hobbies'] as List<dynamic>?)
+                ?.map((e) => e as String)
+                .toList(),
+        imageUrl: json['imageUrl'] 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> _$UserInfoSettingRequestToJson(
+  UserInfoSettingRequest 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,
+  'name': instance.name,
+  'birthday': instance.birthday,
+  'gender': instance.gender,
+  'imageUrl': instance.imageUrl,
+  'hobbies': instance.hobbies,
+  'characters': instance.characters,
+};

+ 20 - 0
lib/data/api/response/character_group_response.g.dart

@@ -0,0 +1,20 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_group_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterGroupResponse _$CharacterGroupResponseFromJson(
+  Map<String, dynamic> json,
+) => CharacterGroupResponse(
+  characterGroups:
+      (json['characterGroups'] as List<dynamic>)
+          .map((e) => CharacterGroupInfo.fromJson(e as Map<String, dynamic>))
+          .toList(),
+);
+
+Map<String, dynamic> _$CharacterGroupResponseToJson(
+  CharacterGroupResponse instance,
+) => <String, dynamic>{'characterGroups': instance.characterGroups};

+ 19 - 0
lib/data/api/response/character_page_response.dart

@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/character_info.dart';
+
+
+part 'character_page_response.g.dart';
+
+@JsonSerializable()
+class CharacterPageResponse {
+  @JsonKey(name: "count")
+  late final int count;
+  @JsonKey(name: "list")
+  late final List<CharacterInfo> characterInfo;
+
+  CharacterPageResponse({required this.count, required this.characterInfo});
+
+  factory CharacterPageResponse.fromJson(Map<String, dynamic> json) =>
+      _$CharacterPageResponseFromJson(json);
+}

+ 21 - 0
lib/data/api/response/character_page_response.g.dart

@@ -0,0 +1,21 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_page_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterPageResponse _$CharacterPageResponseFromJson(
+  Map<String, dynamic> json,
+) => CharacterPageResponse(
+  count: (json['count'] as num).toInt(),
+  characterInfo:
+      (json['list'] as List<dynamic>)
+          .map((e) => CharacterInfo.fromJson(e as Map<String, dynamic>))
+          .toList(),
+);
+
+Map<String, dynamic> _$CharacterPageResponseToJson(
+  CharacterPageResponse instance,
+) => <String, dynamic>{'count': instance.count, 'list': instance.characterInfo};

+ 16 - 0
lib/data/api/response/config_response.dart

@@ -0,0 +1,16 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/config_bean.dart';
+
+part 'config_response.g.dart';
+
+@JsonSerializable()
+class ConfigResponse {
+  @JsonKey(name: "list")
+  List<ConfigBean>? list;
+
+  ConfigResponse(this.list);
+
+  factory ConfigResponse.fromJson(Map<String, dynamic> json) =>
+      _$ConfigResponseFromJson(json);
+}

+ 17 - 0
lib/data/api/response/config_response.g.dart

@@ -0,0 +1,17 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'config_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ConfigResponse _$ConfigResponseFromJson(Map<String, dynamic> json) =>
+    ConfigResponse(
+      (json['list'] as List<dynamic>?)
+          ?.map((e) => ConfigBean.fromJson(e as Map<String, dynamic>))
+          .toList(),
+    );
+
+Map<String, dynamic> _$ConfigResponseToJson(ConfigResponse instance) =>
+    <String, dynamic>{'list': instance.list};

+ 18 - 0
lib/data/api/response/new_user_get_character_response.dart

@@ -0,0 +1,18 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/character_info.dart';
+
+
+part 'new_user_get_character_response.g.dart';
+
+@JsonSerializable()
+class NewUserGetCharacterResponse {
+
+  @JsonKey(name: "list")
+  late final List<CharacterInfo> characterInfo;
+
+  NewUserGetCharacterResponse({ required this.characterInfo});
+
+  factory NewUserGetCharacterResponse.fromJson(Map<String, dynamic> json) =>
+      _$NewUserGetCharacterResponseFromJson(json);
+}

+ 20 - 0
lib/data/api/response/new_user_get_character_response.g.dart

@@ -0,0 +1,20 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'new_user_get_character_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+NewUserGetCharacterResponse _$NewUserGetCharacterResponseFromJson(
+  Map<String, dynamic> json,
+) => NewUserGetCharacterResponse(
+  characterInfo:
+      (json['list'] as List<dynamic>)
+          .map((e) => CharacterInfo.fromJson(e as Map<String, dynamic>))
+          .toList(),
+);
+
+Map<String, dynamic> _$NewUserGetCharacterResponseToJson(
+  NewUserGetCharacterResponse instance,
+) => <String, dynamic>{'list': instance.characterInfo};

+ 59 - 0
lib/data/api/response/user_info_response.dart

@@ -0,0 +1,59 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/member_info.dart';
+
+part 'user_info_response.g.dart';
+
+
+@JsonSerializable()
+class UserInfoResponse {
+  @JsonKey(name: 'userId')
+  String? userId;
+
+  @JsonKey(name: 'ssid')
+  String? ssid;
+
+  @JsonKey(name: 'deviceId')
+  String? deviceId;
+
+  @JsonKey(name: 'phone')
+  String? phone;
+
+  @JsonKey(name: 'loginStatus')
+  int? loginStatus;
+
+  @JsonKey(name: 'channelName')
+  String? channelName;
+
+  @JsonKey(name: 'gender')
+  int? gender;
+
+  @JsonKey(name: 'birthday')
+  String? birthday;
+
+  @JsonKey(name: 'intimacy')
+  int? intimacy;
+
+  @JsonKey(name: 'userIdOrSsid')
+  String? userIdOrSsid;
+
+  @JsonKey(name: 'memberInfo')
+  MemberInfo? memberInfo;
+
+  UserInfoResponse(
+    this.userId,
+    this.ssid,
+    this.deviceId,
+    this.phone,
+    this.loginStatus,
+    this.channelName,
+    this.gender,
+    this.birthday,
+    this.intimacy,
+    this.userIdOrSsid,
+    this.memberInfo,
+  );
+
+  factory UserInfoResponse.fromJson(Map<String, dynamic> json) =>
+      _$UserInfoResponseFromJson(json);
+}

+ 39 - 0
lib/data/api/response/user_info_response.g.dart

@@ -0,0 +1,39 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'user_info_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+UserInfoResponse _$UserInfoResponseFromJson(Map<String, dynamic> json) =>
+    UserInfoResponse(
+      json['userId'] as String?,
+      json['ssid'] as String?,
+      json['deviceId'] as String?,
+      json['phone'] as String?,
+      (json['loginStatus'] as num?)?.toInt(),
+      json['channelName'] as String?,
+      (json['gender'] as num?)?.toInt(),
+      json['birthday'] as String?,
+      (json['intimacy'] as num?)?.toInt(),
+      json['userIdOrSsid'] as String?,
+      json['memberInfo'] == null
+          ? null
+          : MemberInfo.fromJson(json['memberInfo'] as Map<String, dynamic>),
+    );
+
+Map<String, dynamic> _$UserInfoResponseToJson(UserInfoResponse instance) =>
+    <String, dynamic>{
+      'userId': instance.userId,
+      'ssid': instance.ssid,
+      'deviceId': instance.deviceId,
+      'phone': instance.phone,
+      'loginStatus': instance.loginStatus,
+      'channelName': instance.channelName,
+      'gender': instance.gender,
+      'birthday': instance.birthday,
+      'intimacy': instance.intimacy,
+      'userIdOrSsid': instance.userIdOrSsid,
+      'memberInfo': instance.memberInfo,
+    };

+ 2 - 2
lib/data/bean/character_group_info.dart

@@ -14,12 +14,12 @@ class CharacterGroupInfo {
 
   //icon
   @JsonKey(name: 'iconUrl')
-  late String iconUrl;
+  String? iconUrl;
 
   CharacterGroupInfo({
     required this.id,
     required this.name,
-    required this.iconUrl,
+    this.iconUrl,
   });
 
   factory CharacterGroupInfo.fromJson(Map<String, dynamic> json) =>

+ 21 - 0
lib/data/bean/character_group_info.g.dart

@@ -0,0 +1,21 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_group_info.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterGroupInfo _$CharacterGroupInfoFromJson(Map<String, dynamic> json) =>
+    CharacterGroupInfo(
+      id: json['id'] as String,
+      name: json['name'] as String,
+      iconUrl: json['iconUrl'] as String?,
+    );
+
+Map<String, dynamic> _$CharacterGroupInfoToJson(CharacterGroupInfo instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'name': instance.name,
+      'iconUrl': instance.iconUrl,
+    };

+ 51 - 0
lib/data/bean/character_info.dart

@@ -0,0 +1,51 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'character_info.g.dart';
+
+@JsonSerializable()
+class CharacterInfo {
+  //人设id
+  @JsonKey(name: 'id')
+  String? id;
+
+  //人设名称
+  @JsonKey(name: 'name')
+  String? name;
+
+  //头像
+  @JsonKey(name: 'imageUrl')
+  String? imageUrl;
+
+  // 人设描述
+  @JsonKey(name: 'description')
+  String? description;
+
+  // 人设表情
+  @JsonKey(name: 'emoji')
+  String? emoji;
+
+  @JsonKey(name: 'isVip')
+  bool? isVip;
+
+  @JsonKey(name: 'isLock')
+  bool? isLock;
+
+  @JsonKey(name: 'isAdd')
+  bool? isAdd;
+
+  CharacterInfo({
+    this.id,
+    this.name,
+    this.imageUrl,
+    this.description,
+    this.emoji,
+    this.isVip,
+    this.isLock,
+    this.isAdd,
+  });
+
+  factory CharacterInfo.fromJson(Map<String, dynamic> json) =>
+      _$CharacterInfoFromJson(json);
+
+  Map<String, dynamic> toJson() => _$CharacterInfoToJson(this);
+}

+ 31 - 0
lib/data/bean/character_info.g.dart

@@ -0,0 +1,31 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_info.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterInfo _$CharacterInfoFromJson(Map<String, dynamic> json) =>
+    CharacterInfo(
+      id: json['id'] as String?,
+      name: json['name'] as String?,
+      imageUrl: json['imageUrl'] as String?,
+      description: json['description'] as String?,
+      emoji: json['emoji'] as String?,
+      isVip: json['isVip'] as bool?,
+      isLock: json['isLock'] as bool?,
+      isAdd: json['isAdd'] as bool?,
+    );
+
+Map<String, dynamic> _$CharacterInfoToJson(CharacterInfo instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'name': instance.name,
+      'imageUrl': instance.imageUrl,
+      'description': instance.description,
+      'emoji': instance.emoji,
+      'isVip': instance.isVip,
+      'isLock': instance.isLock,
+      'isAdd': instance.isAdd,
+    };

+ 19 - 0
lib/data/bean/config_bean.dart

@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'config_bean.g.dart';
+
+@JsonSerializable()
+class ConfigBean {
+  @JsonKey(name: 'confCode')
+  String confCode;
+
+  @JsonKey(name: 'value')
+  dynamic value;
+
+  ConfigBean(this.confCode, this.value);
+
+  factory ConfigBean.fromJson(Map<String, dynamic> json) =>
+      _$ConfigBeanFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ConfigBeanToJson(this);
+}

+ 13 - 0
lib/data/bean/config_bean.g.dart

@@ -0,0 +1,13 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'config_bean.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ConfigBean _$ConfigBeanFromJson(Map<String, dynamic> json) =>
+    ConfigBean(json['confCode'] as String, json['value']);
+
+Map<String, dynamic> _$ConfigBeanToJson(ConfigBean instance) =>
+    <String, dynamic>{'confCode': instance.confCode, 'value': instance.value};

+ 19 - 0
lib/data/bean/member_info.dart

@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'member_info.g.dart';
+
+@JsonSerializable()
+class MemberInfo {
+  @JsonKey(name: 'isMember')
+  bool isMember;
+
+  @JsonKey(name: 'endTimestamp')
+  int? endTimestamp;
+
+  MemberInfo(this.isMember, this.endTimestamp);
+
+  factory MemberInfo.fromJson(Map<String, dynamic> json) =>
+      _$MemberInfoFromJson(json);
+
+  Map<String, dynamic> toJson() => _$MemberInfoToJson(this);
+}

+ 18 - 0
lib/data/bean/member_info.g.dart

@@ -0,0 +1,18 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'member_info.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+MemberInfo _$MemberInfoFromJson(Map<String, dynamic> json) => MemberInfo(
+  json['isMember'] as bool,
+  (json['endTimestamp'] as num?)?.toInt(),
+);
+
+Map<String, dynamic> _$MemberInfoToJson(MemberInfo instance) =>
+    <String, dynamic>{
+      'isMember': instance.isMember,
+      'endTimestamp': instance.endTimestamp,
+    };

+ 82 - 19
lib/data/repository/account_repository.dart

@@ -1,8 +1,13 @@
+import 'dart:async';
+
 import 'package:get/get.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/data/api/atmob_api.dart';
+import 'package:keyboard/data/api/request/user_info_setting_request.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';
 import '../../utils/mmkv_util.dart';
@@ -10,6 +15,7 @@ import '../api/request/complaint_submit_request.dart';
 import '../api/request/login_request.dart';
 import '../api/request/send_code_request.dart';
 import '../api/response/login_response.dart';
+import '../api/response/user_info_response.dart';
 import '../consts/error_code.dart';
 
 @lazySingleton
@@ -19,12 +25,19 @@ class AccountRepository {
   static final String keyAccountLoginPhoneNum = 'key_account_login_phone_num';
   static final String keyAccountLoginToken = 'key_account_login_token';
 
+  final Rxn<UserInfoResponse> _userInfo = Rxn<UserInfoResponse>();
+
+  Rxn<UserInfoResponse> get userInfo => _userInfo;
+
   RxnString loginPhoneNum = RxnString();
   RxBool isLogin = RxBool(false);
 
   int? _lastRequestCodeTime;
   int _errorCodeTimes = 0;
 
+  Timer? refreshUserInfoHandler;
+  CancelableFuture? userInfoFuture;
+
   static String? token = KVUtil.getString(keyAccountLoginToken, null);
 
   AccountRepository(this.atmobApi) {
@@ -35,11 +48,14 @@ class AccountRepository {
       }),
     );
     loginPhoneNum.value = KVUtil.getString(keyAccountLoginPhoneNum, null);
+    refreshUserInfo();
   }
 
   // 检查是否在 60 秒内重复请求
   Future<void> loginSendCode(String phoneNum) {
-    final currentTime = DateTime.now().millisecondsSinceEpoch;
+    final currentTime = DateTime
+        .now()
+        .millisecondsSinceEpoch;
     if (currentTime - (_lastRequestCodeTime ?? 0) < 60 * 1000) {
       throw RequestCodeTooOftenException();
     }
@@ -47,15 +63,13 @@ class AccountRepository {
         .loginSendCode(SendCodeRequest(phoneNum))
         .then(HttpHandler.handle(true))
         .then((value) {
-          _lastRequestCodeTime = currentTime;
-          _errorCodeTimes = 0;
-        });
+      _lastRequestCodeTime = currentTime;
+      _errorCodeTimes = 0;
+    });
   }
 
-  Future<LoginResponse> loginUserLogin(
-    String phoneNum,
-    String verificationCode,
-  ) {
+  Future<LoginResponse> loginUserLogin(String phoneNum,
+      String verificationCode,) {
     if (_errorCodeTimes >= 5) {
       throw LoginTooOftenException();
     }
@@ -63,17 +77,66 @@ class AccountRepository {
         .loginUserLogin(LoginRequest(phoneNum, verificationCode))
         .then(HttpHandler.handle(true))
         .then((response) {
-          _errorCodeTimes = 0;
-          onLoginSuccess(phoneNum, response.authToken);
-          return response;
-        })
+      _errorCodeTimes = 0;
+      onLoginSuccess(phoneNum, response.authToken);
+      return response;
+    })
+        .catchError((error) {
+      if (error is ServerErrorException &&
+          error.code == ErrorCode.verificationCodeError) {
+        _errorCodeTimes++;
+      }
+      throw error;
+    });
+  }
+
+  void refreshUserInfo() {
+    userInfoFuture?.cancel();
+    userInfoFuture = AsyncUtil.retryWithExponentialBackoff(
+          () => getUserInfo(),
+      10,
+      predicate: (error) {
+        if (error is ServerErrorException) {
+          return error.code != ErrorCode.noLoginError;
+        }
+        return true;
+      },
+    );
+    userInfoFuture
+        ?.then((userInfo) {
+      AtmobLog.d(tag, "refreshUserInfo success");
+    })
         .catchError((error) {
-          if (error is ServerErrorException &&
-              error.code == ErrorCode.verificationCodeError) {
-            _errorCodeTimes++;
-          }
-          throw error;
-        });
+      AtmobLog.e(tag, "refreshUserInfo error: $error");
+    });
+  }
+
+  Future<UserInfoResponse> getUserInfo() {
+    return atmobApi
+        .getUserInfo(AppBaseRequest())
+        .then(HttpHandler.handle(true))
+        .then((response) {
+      _userInfo.value = response;
+
+      return response;
+    });
+  }
+
+  Future<void> setUserInfo(
+      {String? name, String? birthday, int? gender, String? imageUrl, List<
+          String>?hobbies, List<String>?characters}) {
+    return atmobApi
+        .setUserInfo(
+      UserInfoSettingRequest(
+          name: name,
+          birthday: birthday,
+          gender: gender,
+          imageUrl: imageUrl,
+          hobbies: hobbies,
+          characters:characters
+      ),
+    )
+        .then(HttpHandler.handle(true));
   }
 
   void onLoginSuccess(String phoneNum, String authToken) {
@@ -82,7 +145,7 @@ class AccountRepository {
 
     AccountRepository.token = authToken;
     loginPhoneNum.value = phoneNum;
-
+    refreshUserInfo();
     KVUtil.putString(keyAccountLoginPhoneNum, phoneNum);
     KVUtil.putString(keyAccountLoginToken, authToken);
   }

+ 33 - 2
lib/data/repository/characters_repository.dart

@@ -1,25 +1,56 @@
 import 'package:injectable/injectable.dart';
 
+import 'package:get/get.dart';
 import '../../base/app_base_request.dart';
 import '../../utils/atmob_log.dart';
 import '../../utils/http_handler.dart';
 import '../api/atmob_api.dart';
+import '../api/request/character_page_request.dart';
 import '../api/response/character_group_response.dart';
+import '../api/response/character_page_response.dart';
+import '../bean/character_group_info.dart';
 
 @lazySingleton
 class CharactersRepository {
   final AtmobApi atmobApi;
   final String tag = "CharactersRepository";
 
+  final RxList<CharacterGroupInfo> _characterGroupList =
+      RxList<CharacterGroupInfo>();
+
+  RxList<CharacterGroupInfo> get characterGroupList => _characterGroupList;
+
   CharactersRepository(this.atmobApi) {
     AtmobLog.d(tag, '$tag....init');
   }
 
-
-
+  // 获取主题
   Future<CharacterGroupResponse> getCharactersGroup() {
     return atmobApi
         .getCharactersGroup(AppBaseRequest())
+        .then(HttpHandler.handle(true))
+        .then((response) {
+          _characterGroupList.value = response.characterGroups;
+          return response;
+        });
+  }
+
+  //   获取人设列表
+  Future<CharacterPageResponse> getCharactersPage(
+    String groupId,
+    int page,
+    String keyboardId,
+    int pageSize,
+  ) {
+    return atmobApi
+        .getCharactersPage(
+          CharacterPageRequest(
+            keyboardId: keyboardId,
+            groupId: groupId,
+            page: page,
+            pageSize: pageSize,
+          ),
+        )
         .then(HttpHandler.handle(true));
   }
 }

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

@@ -0,0 +1,41 @@
+import 'package:injectable/injectable.dart';
+
+import '../../utils/async_util.dart';
+import '../../utils/atmob_log.dart';
+import '../../utils/http_handler.dart';
+import '../api/atmob_api.dart';
+import '../api/request/config_request.dart';
+import '../api/response/config_response.dart';
+
+@lazySingleton
+class ConfigRepository {
+  final tag = "ConfigRepository";
+  final AtmobApi atmobApi;
+
+  ConfigRepository(this.atmobApi) {
+    AtmobLog.d(tag, '$tag....init');
+    refreshConfig();
+  }
+
+  // 更新配置的值
+  void refreshConfig() {
+    AsyncUtil.retry(
+      () => requestConfigsData(),
+      Duration(seconds: 3),
+      maxRetry: 100,
+    ).then((configsResponse) {
+      final list = configsResponse.list;
+      if (list == null || list.isEmpty) {
+        return;
+      }
+      AtmobLog.d(tag, 'refreshConfig....list: $list');
+    });
+  }
+
+  /// 获取配置信息
+  Future<ConfigResponse> requestConfigsData() {
+    return atmobApi
+        .confs(ConfigRequest(confCodes: ['intimacy', 'default_avatar']))
+        .then(HttpHandler.handle(true));
+  }
+}

+ 8 - 1
lib/di/get_it.config.dart

@@ -18,6 +18,7 @@ import '../data/api/atmob_stream_api.dart' as _i329;
 import '../data/repository/account_repository.dart' as _i83;
 import '../data/repository/characters_repository.dart' as _i421;
 import '../data/repository/chat_repository.dart' as _i425;
+import '../data/repository/config_repository.dart' as _i50;
 import '../module/about/about_controller.dart' as _i256;
 import '../module/browser/browser_controller.dart' as _i923;
 import '../module/character/character_controller.dart' as _i888;
@@ -70,6 +71,9 @@ extension GetItInjectableX on _i174.GetIt {
     gh.lazySingleton<_i421.CharactersRepository>(
       () => _i421.CharactersRepository(gh<_i243.AtmobApi>()),
     );
+    gh.lazySingleton<_i50.ConfigRepository>(
+      () => _i50.ConfigRepository(gh<_i243.AtmobApi>()),
+    );
     gh.factory<_i876.FeedbackController>(
       () => _i876.FeedbackController(gh<_i83.AccountRepository>()),
     );
@@ -77,7 +81,10 @@ extension GetItInjectableX on _i174.GetIt {
       () => _i732.MineController(gh<_i83.AccountRepository>()),
     );
     gh.factory<_i888.CharacterController>(
-      () => _i888.CharacterController(gh<_i421.CharactersRepository>()),
+      () => _i888.CharacterController(
+        gh<_i421.CharactersRepository>(),
+        gh<_i50.ConfigRepository>(),
+      ),
     );
     return this;
   }

+ 93 - 6
lib/module/character/character_controller.dart

@@ -1,26 +1,88 @@
-import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
-import 'package:keyboard/data/api/response/character_group_response.dart';
+import 'package:keyboard/data/repository/config_repository.dart';
+import 'package:keyboard/utils/atmob_log.dart';
 
+import '../../data/bean/character_group_info.dart';
 import '../../data/repository/characters_repository.dart';
 
 @injectable
-class CharacterController extends BaseController {
+class CharacterController extends BaseController
+    with GetTickerProviderStateMixin {
   final String tag = "CharacterController";
   final CharactersRepository charactersRepository;
+  final ConfigRepository configRepository;
 
-  CharacterController(this.charactersRepository);
+  CharacterController(this.charactersRepository,this.configRepository);
+
+  RxList<CharacterGroupInfo> get characterGroupList =>
+      charactersRepository.characterGroupList;
+
+  late Rx<TabController> tabController;
+  late PageController pageController;
+  RxInt currentTabBarIndex = 0.obs;
+
+  RxList<String> keyboardOptions = ["默认键盘", "新建键盘1"].obs;
+
+  Rx<String?> selectedValue = Rx<String?>(null);
 
   @override
   void onInit() {
     super.onInit();
+    _dataLoad();
+  }
+
 
-    charactersRepository.getCharactersGroup().then((CharacterGroupResponse value) {
-      debugPrint(value.characterGroups.toString());
+
+  void _dataLoad() async {
+    charactersRepository.getCharactersGroup();
+    pageController = PageController();
+    tabController =
+        TabController(
+          length: characterGroupList.length,
+          vsync: this,
+          initialIndex: 0,
+        ).obs;
+
+    ever(charactersRepository.characterGroupList, (value) {
+      AtmobLog.d(tag, "characterGroupList changed");
+      if (value.isNotEmpty) {
+        tabController.value = TabController(
+          length: characterGroupList.length,
+          vsync: this,
+          initialIndex: 0,
+        );
+      }
     });
   }
 
+  void onTabChanged(int index) {
+    if (index >= characterGroupList.length) {
+      return;
+    }
+    AtmobLog.d(tag, "onTabChanged index:$index");
+    pageController.animateToPage(
+      index,
+      duration: const Duration(milliseconds: 300),
+      curve: Curves.easeInToLinear,
+    );
+  }
+
+  void onPageChanged(int index) {
+
+    if (index >= characterGroupList.length) {
+      return;
+    }
+    AtmobLog.d(tag, "onPageChanged index:$index");
+    currentTabBarIndex.value = index;
+    tabController.value.animateTo(
+      index,
+      duration: const Duration(milliseconds: 300),
+    );
+  }
+
   @override
   void onReady() {
     super.onReady();
@@ -28,6 +90,31 @@ class CharacterController extends BaseController {
 
   @override
   void onClose() {
+    pageController.dispose();
+    tabController.value.dispose();
     super.onClose();
   }
+
+  clickMyKeyboard() {
+    AtmobLog.d(tag, "clickMyKeyboard");
+  }
+
+  void updateSelectedValue(String? newValue) {
+    selectedValue.value = newValue;
+  }
+
+  // 添加一个新的键盘
+  void addKeyboard(String name) {
+    keyboardOptions.add(name);
+  }
+
+  // 删除某个键盘
+  void removeKeyboard(String name) {
+    keyboardOptions.remove(name);
+    // 如果删除的是当前选中的值,则重置
+    if (selectedValue.value == name) {
+      selectedValue.value =
+          keyboardOptions.isNotEmpty ? keyboardOptions.first : null;
+    }
+  }
 }

+ 431 - 4
lib/module/character/character_view.dart

@@ -1,16 +1,443 @@
-import 'package:flutter/cupertino.dart';
 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 'character_controller.dart';
 
 class CharacterView extends BaseView<CharacterController> {
   const CharacterView({super.key});
 
-
+  @override
+  backgroundColor() {
+    return Colors.transparent;
+  }
 
   @override
   Widget buildBody(BuildContext context) {
-    return Center(child: Text("Character", style: TextStyle(color: Colors.black)));
+    return Scaffold(
+      backgroundColor: Color(0xFFF6F5FA),
+      body: Builder(
+        builder: (context) {
+          return NestedScrollView(
+            headerSliverBuilder: (context, innerBoxIsScrolled) {
+              return [
+                /// **🔹 让背景图滑动时裁剪掉上方部分**
+                SliverPersistentHeader(
+                  pinned: true,
+                  delegate: CharacterHeaderDelegate(
+                    expandedHeight: 240.h,
+                    minHeight: 100.h,
+                    // bottomWidget: SizedBox(),
+                    onTap: controller.clickMyKeyboard,
+                  ),
+                ),
+                SliverPersistentHeader(
+                  pinned: true,
+
+                  // floating: true,
+                  delegate: TabBarDelegate(
+                    height: 180.h,
+                    child: _bottomAppBar(),
+                  ),
+                ),
+              ];
+            },
+
+            body: _pages(),
+          );
+        },
+      ),
+    );
+  }
+
+  /// **自定义 bottomAppBar**
+  Widget _bottomAppBar() {
+    return Container(
+      decoration: ShapeDecoration(
+        gradient: LinearGradient(
+          begin: Alignment(0.50, -0.00),
+          end: Alignment(0.50, 1.00),
+          colors: [Color(0xFFEAE5FF), Color(0xFFF5F4F9)],
+        ),
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.only(
+            topLeft: Radius.circular(20.r),
+            topRight: Radius.circular(20.r),
+          ),
+        ),
+      ),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.min,
+        children: [
+          _customizeButton(),
+          SizedBox(height: 14.h),
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              Assets.images.iconCharacterMarket.image(
+                width: 73.w,
+                height: 25.h,
+              ),
+              Obx(() {
+                return DropdownButton<String>(
+                  // hint: Text(''),
+                  underline: Container(height: 0),
+                  style: TextStyle(
+                    color: Colors.black.withAlpha(102),
+                    fontSize: 14.sp,
+                    fontWeight: FontWeight.w400,
+                  ),
+
+                  icon: Assets.images.iconCharacterArrowDown.image(
+                    width: 20.r,
+                    height: 20.r,
+                  ),
+                  value: controller.selectedValue.value,
+                  // 绑定 selectedValue
+                  onChanged: (String? newValue) {
+                    controller.updateSelectedValue(newValue); // 更新选中的值
+                  },
+
+                  items: List.generate(controller.keyboardOptions.length, (
+                    index,
+                  ) {
+                    String value = controller.keyboardOptions[index];
+                    return DropdownMenuItem<String>(
+                      value: value,
+                      child: Column(
+                        crossAxisAlignment: CrossAxisAlignment.start,
+                        // 让选项文本左对齐
+                        mainAxisSize: MainAxisSize.min,
+                        // 让 Column 适配内容
+                        children: [
+                          Padding(
+                            padding: EdgeInsets.symmetric(vertical: 8),
+                            child: Text(
+                              value,
+                              style: TextStyle(
+                                color: Colors.black.withAlpha(204),
+                                fontSize: 14.sp,
+                                fontWeight: FontWeight.w400,
+                              ),
+                            ),
+                          ),
+                          if (index != controller.keyboardOptions.length - 1)
+                            Divider(
+                              color: Color(0xFFF6F6F6),
+                              thickness: 1,
+                              height: 1,
+                            ),
+                        ],
+                      ),
+                    );
+                  }),
+                );
+              }),
+            ],
+          ),
+          SizedBox(height: 15.h),
+          tabBar(),
+        ],
+      ),
+    );
+  }
+
+  // 定制按钮
+  Widget _customizeButton() {
+    return Container(
+      margin: EdgeInsets.only(left: 16.w),
+      width: 220.w,
+      height: 56.h,
+      padding: EdgeInsets.symmetric(horizontal: 10.w),
+      decoration: ShapeDecoration(
+        color: const Color(0xFF121212),
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(40.r),
+        ),
+      ),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          Assets.images.iconCharacterCustomized.image(
+            width: 36.r,
+            height: 36.r,
+          ),
+          SizedBox(width: 8.w),
+          Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: [
+              Text(
+                StringName.goCustomizeCharacter,
+                textAlign: TextAlign.center,
+                style: TextStyle(
+                  color: Colors.white,
+                  fontSize: 16.sp,
+                  fontWeight: FontWeight.w500,
+                ),
+              ),
+              Text(
+                StringName.goCustomizeCharacterDesc,
+                style: TextStyle(
+                  color: Color(0xFFF5F4F9),
+                  fontSize: 11.sp,
+                  fontWeight: FontWeight.w400,
+                ),
+              ),
+            ],
+          ),
+          Container(
+            margin: EdgeInsets.only(left: 16.w),
+            width: 24.r,
+            height: 24.r,
+            decoration: ShapeDecoration(
+              color: Colors.white,
+              shape: OvalBorder(),
+            ),
+            child: Assets.images.iconCharacterArrowRight.image(
+              width: 16.r,
+              height: 16.r,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// **TabBar**
+  Widget tabBar() {
+    return Obx(() {
+      if (controller.characterGroupList.isEmpty) {
+        return const SizedBox.shrink();
+      }
+      return TabBar(
+        controller: controller.tabController.value,
+        dividerHeight: 0,
+        tabAlignment: TabAlignment.start,
+        isScrollable: true,
+        padding: EdgeInsets.symmetric(horizontal: 12.w),
+        labelPadding: EdgeInsets.symmetric(horizontal: 4.w),
+        indicator: const BoxDecoration(),
+        onTap: (index) => controller.onTabChanged(index),
+        tabs: List.generate(controller.characterGroupList.length, (index) {
+          var e = controller.characterGroupList[index];
+          bool isSelected = index == controller.currentTabBarIndex.value;
+          return Container(
+            width: 80.w,
+            height: isSelected ? 38.h : 32.h,
+            decoration:
+                isSelected
+                    ? BoxDecoration(
+                      borderRadius: BorderRadius.circular(36.r),
+                      image: DecorationImage(
+                        image:
+                            Assets.images.iconCharacterGroupSelected.provider(),
+                        fit: BoxFit.cover,
+                      ),
+                    )
+                    : BoxDecoration(
+                      color: Colors.white.withAlpha(204),
+                      borderRadius: BorderRadius.circular(36.r),
+                    ),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                if (e.iconUrl != null)
+                  Image.network(e.iconUrl!, width: 20.r, height: 20.r),
+                Text(
+                  e.name,
+                  style: TextStyle(
+                    color:
+                        isSelected ? Colors.black : Colors.black.withAlpha(104),
+                    fontSize: 14.sp,
+                    fontWeight: FontWeight.w500,
+                  ),
+                ),
+              ],
+            ),
+          );
+        }),
+      );
+    });
+  }
+
+  Widget _pages() {
+    return Obx(() {
+      if (controller.characterGroupList.isEmpty) {
+        return const Center(child: CircularProgressIndicator());
+      }
+      return PageView(
+        controller: controller.pageController,
+        onPageChanged: (index) {
+          controller.onPageChanged(index);
+        },
+        children:
+            controller.characterGroupList.map((group) {
+              return ListView.builder(
+                itemCount: controller.characterGroupList.length,
+                itemBuilder: (context, index) {
+                  return ListTile(title: Text(group.name));
+                },
+              );
+            }).toList(),
+      );
+    });
+  }
+}
+
+/// **🔹 让背景图滑动时裁剪掉上方部分**
+class CharacterHeaderDelegate extends SliverPersistentHeaderDelegate {
+  final double expandedHeight;
+  final double minHeight;
+
+  // final Widget bottomWidget;
+  final VoidCallback onTap;
+
+  CharacterHeaderDelegate({
+    required this.expandedHeight,
+    required this.minHeight,
+    // required this.bottomWidget,
+    required this.onTap,
+  });
+
+  @override
+  Widget build(
+    BuildContext context,
+    double shrinkOffset,
+    bool overlapsContent,
+  ) {
+    final currentVisibleHeight = (expandedHeight - shrinkOffset).clamp(
+      minHeight,
+      expandedHeight,
+    );
+    final tabBarOffset = expandedHeight - currentVisibleHeight; // 计算 TabBar 位移
+
+    final opacity = 1 - currentVisibleHeight / expandedHeight;
+    return Stack(
+      clipBehavior: Clip.none,
+      children: [
+        // 背景图片,动态裁剪
+        Positioned(
+          top: 0,
+          left: 0,
+          right: 0,
+          height: currentVisibleHeight,
+          child: ClipRect(
+            child: Image.asset(
+              Assets.images.bgCharacterBoyBanner.path,
+              fit: BoxFit.cover,
+              height: expandedHeight,
+              alignment: Alignment.topCenter,
+            ),
+          ),
+        ),
+
+        // 遮罩层 Positioned(用于控制背景的可见性)
+        Positioned(
+          top: 0,
+          left: 0,
+          right: 0,
+          height: currentVisibleHeight,
+          child: Container(color: Colors.black.withValues(alpha: opacity)),
+        ),
+        Positioned(
+          top: 0,
+          child: SafeArea(
+            child: GestureDetector(
+              onTap: onTap,
+              child: Container(
+                margin: EdgeInsets.symmetric(horizontal: 16.w),
+                width: 96.w,
+                height: 32.h,
+                padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
+                decoration: ShapeDecoration(
+                  color: Colors.white.withValues(alpha: 153),
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(10),
+                  ),
+                ),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  spacing: 4.r,
+                  children: [
+                    Container(
+                      width: 24.r,
+                      height: 24.r,
+                      clipBehavior: Clip.antiAlias,
+                      decoration: BoxDecoration(),
+                      child: Assets.images.iconCharacterKeyboard.image(
+                        width: 24.r,
+                        height: 24.r,
+                      ),
+                    ),
+                    Text(
+                      StringName.myKeyboard,
+                      textAlign: TextAlign.center,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(204),
+                        fontSize: 14.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+          ),
+        ),
+        // TabBar 定位
+        // Positioned(
+        //   bottom: tabBarOffset,
+        //   left: 0,
+        //   right: 0,
+        //   child: Transform.translate(
+        //     offset: Offset(0, tabBarOffset),
+        //     child: bottomWidget,
+        //   ),
+        // ),
+      ],
+    );
+  }
+
+  @override
+  double get maxExtent => expandedHeight;
+
+  @override
+  double get minExtent => minHeight;
+
+  @override
+  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
+      true;
+}
+
+class TabBarDelegate extends SliverPersistentHeaderDelegate {
+  final Widget child;
+  final double height;
+
+  TabBarDelegate({required this.child, required this.height});
+
+  @override
+  Widget build(
+    BuildContext context,
+    double shrinkOffset,
+    bool overlapsContent,
+  ) {
+    return SizedBox(height: height, child: child);
   }
-}
+
+  @override
+  double get maxExtent => height; // 固定最大高度
+
+  @override
+  double get minExtent => height; // 固定最小高度
+
+  @override
+  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
+      true;
+}

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

@@ -65,6 +65,8 @@ class MineController extends BaseController {
 
   clickPersonalProfile() {
     debugPrint('clickPersonalProfile');
+    accountRepository.setUserInfo(name: "老铁",birthday: "2021-03-17",gender: 1,hobbies: ["a","b","c"],characters: ["测试"]);
+
   }
 
   clickFeedback() {

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

@@ -35,6 +35,30 @@ class $AssetsImagesGen {
   AssetGenImage get iconBlackBack =>
       const AssetGenImage('assets/images/icon_black_back.webp');
 
+  /// File path: assets/images/icon_character_arrow_down.webp
+  AssetGenImage get iconCharacterArrowDown =>
+      const AssetGenImage('assets/images/icon_character_arrow_down.webp');
+
+  /// File path: assets/images/icon_character_arrow_right.webp
+  AssetGenImage get iconCharacterArrowRight =>
+      const AssetGenImage('assets/images/icon_character_arrow_right.webp');
+
+  /// File path: assets/images/icon_character_customized.webp
+  AssetGenImage get iconCharacterCustomized =>
+      const AssetGenImage('assets/images/icon_character_customized.webp');
+
+  /// File path: assets/images/icon_character_group_selected.webp
+  AssetGenImage get iconCharacterGroupSelected =>
+      const AssetGenImage('assets/images/icon_character_group_selected.webp');
+
+  /// File path: assets/images/icon_character_keyboard.webp
+  AssetGenImage get iconCharacterKeyboard =>
+      const AssetGenImage('assets/images/icon_character_keyboard.webp');
+
+  /// File path: assets/images/icon_character_market.webp
+  AssetGenImage get iconCharacterMarket =>
+      const AssetGenImage('assets/images/icon_character_market.webp');
+
   /// File path: assets/images/icon_mine_about.webp
   AssetGenImage get iconMineAbout =>
       const AssetGenImage('assets/images/icon_mine_about.webp');
@@ -124,6 +148,12 @@ class $AssetsImagesGen {
     bgMineVipCard,
     iconAboutArrowLeft,
     iconBlackBack,
+    iconCharacterArrowDown,
+    iconCharacterArrowRight,
+    iconCharacterCustomized,
+    iconCharacterGroupSelected,
+    iconCharacterKeyboard,
+    iconCharacterMarket,
     iconMineAbout,
     iconMineArrow,
     iconMineBackArrow,

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

@@ -0,0 +1,106 @@
+import 'package:get/get.dart';
+
+class StringName {
+  StringName._();
+  static final String appName = 'app_name'.tr; // 追爱小键盘
+  static final String mainTabKeyboard = 'main_tab_keyboard'.tr; // 键盘
+  static final String mainTabMine = 'main_tab_mine'.tr; // 我的
+  static final String mainTabCharacter = 'main_tab_character'.tr; // 人设
+  static final String onlineCustomerService = 'online_customer_service'.tr; // 在线客服
+  static final String tutorials = 'tutorials'.tr; // 使用教程
+  static final String personalProfile = 'personal_profile'.tr; // 个人档案
+  static final String feedback = 'feedback'.tr; // 意见反馈
+  static final String aboutUs = 'about_us'.tr; // 关于我们
+  static final String mineAccountLoggedDesc = 'mine_account_logged_desc'.tr; // 用户
+  static final String mineAccountNoLogin = 'mine_account_no_login'.tr; // 游客,去登陆
+  static final String directSend = 'direct_send'.tr; // 生成内容直接发送
+  static final String directSendDesc = 'direct_send_desc'.tr; // 内容生成后,点击发送按钮,直接发送
+  static final String openFloat = 'open_float'.tr; // 打开悬浮窗
+  static final String openFloatDesc = 'open_float_desc'.tr; // 设置悬浮窗权限
+  static final String autoOpenFloat = 'auto_open_float'.tr; // 自动打开浮窗
+  static final String autoOpenFloatDesc = 'auto_open_float_desc'.tr; // 在启动键盘和APP时,自动打开悬浮窗
+  static final String vipLevel0Desc = 'vip_level0_desc'.tr; // 开通会员,甜爱脱单只差一步
+  static final String vipLevel1Desc = 'vip_level1_desc'.tr; // 会员有效期至
+  static final String vipLevel2Desc = 'vip_level2_desc'.tr; // 您已是甜爱键盘终身会员
+  static final String vipLevel0Btn = 'vip_level0_btn'.tr; // 立即开通
+  static final String vipLevel1Btn = 'vip_level1_btn'.tr; // 立即续费
+  static final String vipLevel2Btn = 'vip_level2_btn'.tr; // 立即查看
+  static final String networkError = 'network_error'.tr; // 网络异常
+  static final String accountNoLogin = 'account_no_login'.tr; // 账号未登录
+  static final String loginAccount = 'login_account'.tr; // 登录账号
+  static final String loginRequestCodeFrequentlyToast = 'login_request_code_frequently_toast'.tr; // 请求过于频繁,请稍后再试
+  static final String loginVerificationCodeRequestFailedToast = 'login_verification_code_request_failed_toast'.tr; // 验证码发送失败,请重试
+  static final String loginSuccess = 'login_success'.tr; // 登录成功
+  static final String loginTooOftenToast = 'login_too_often_toast'.tr; // 登录过于频繁,请稍后再试
+  static final String loginFailedToast = 'login_failed_toast'.tr; // 登录失败
+  static final String loginVerificationCodeErrorToast = 'login_verification_code_error_toast'.tr; // 验证码输入错误,请重新输入
+  static final String feedbackContentTitle = 'feedback_content_title'.tr; // 问题描述
+  static final String feedbackContentHint = 'feedback_content_hint'.tr; // 请描述您的问题或建议,或者您可以联系我们在线客服
+  static final String feedbackPhone = 'feedback_phone'.tr; // 联系电话
+  static final String feedbackPhoneHint = 'feedback_phone_hint'.tr; // 填写手机号,方便更快为你解决问题
+  static final String feedbackSubmit = 'feedback_submit'.tr; // 提交
+  static final String feedbackContentEmpty = 'feedback_content_empty'.tr; // 请输入反馈内容
+  static final String feedbackPhoneEmpty = 'feedback_phone_empty'.tr; // 请输入联系电话
+  static final String feedbackSubmitSuccess = 'feedback_submit_success'.tr; // 感谢您的意见与反馈
+  static final String currentVersion = 'current_version'.tr; // 当前版本
+  static final String privacyPolicy = 'privacy_policy'.tr; // 隐私政策
+  static final String serviceTerms = 'service_terms'.tr; // 服务条款
+  static final String childPrivacyPolicy = 'child_privacy_policy'.tr; // 儿童隐私保护协议
+  static final String goCustomizeCharacter = 'go_customize_character'.tr; // 去定制人设
+  static final String goCustomizeCharacterDesc = 'go_customize_character_desc'.tr; // 定制你自己的专属人设
+  static final String myKeyboard = 'my_keyboard'.tr; // 我的键盘
+}
+class StringMultiSource {
+  StringMultiSource._();
+  static const Map<String, Map<String, String>> values = {
+    'zh_CN': {
+      'app_name': '追爱小键盘',
+      'main_tab_keyboard': '键盘',
+      'main_tab_mine': '我的',
+      'main_tab_character': '人设',
+      'online_customer_service': '在线客服',
+      'tutorials': '使用教程',
+      'personal_profile': '个人档案',
+      'feedback': '意见反馈',
+      'about_us': '关于我们',
+      'mine_account_logged_desc': '用户',
+      'mine_account_no_login': '游客,去登陆',
+      'direct_send': '生成内容直接发送',
+      'direct_send_desc': '内容生成后,点击发送按钮,直接发送',
+      'open_float': '打开悬浮窗',
+      'open_float_desc': '设置悬浮窗权限',
+      'auto_open_float': '自动打开浮窗',
+      'auto_open_float_desc': '在启动键盘和APP时,自动打开悬浮窗',
+      'vip_level0_desc': '开通会员,甜爱脱单只差一步',
+      'vip_level1_desc': '会员有效期至',
+      'vip_level2_desc': '您已是甜爱键盘终身会员',
+      'vip_level0_btn': '立即开通',
+      'vip_level1_btn': '立即续费',
+      'vip_level2_btn': '立即查看',
+      'network_error': '网络异常',
+      'account_no_login': '账号未登录',
+      'login_account': '登录账号',
+      'login_request_code_frequently_toast': '请求过于频繁,请稍后再试',
+      'login_verification_code_request_failed_toast': '验证码发送失败,请重试',
+      'login_success': '登录成功',
+      'login_too_often_toast': '登录过于频繁,请稍后再试',
+      'login_failed_toast': '登录失败',
+      'login_verification_code_error_toast': '验证码输入错误,请重新输入',
+      'feedback_content_title': '问题描述',
+      'feedback_content_hint': '请描述您的问题或建议,或者您可以联系我们在线客服',
+      'feedback_phone': '联系电话',
+      'feedback_phone_hint': '填写手机号,方便更快为你解决问题',
+      'feedback_submit': '提交',
+      'feedback_content_empty': '请输入反馈内容',
+      'feedback_phone_empty': '请输入联系电话',
+      'feedback_submit_success': '感谢您的意见与反馈',
+      'current_version': '当前版本',
+      'privacy_policy': '隐私政策',
+      'service_terms': '服务条款',
+      'child_privacy_policy': '儿童隐私保护协议',
+      'go_customize_character': '去定制人设',
+      'go_customize_character_desc': '定制你自己的专属人设',
+      'my_keyboard': '我的键盘',
+    },
+  };
+}

+ 3 - 0
pubspec.yaml

@@ -49,6 +49,9 @@ dependencies:
   flutter_localizations:
     sdk: flutter
 
+  #网络图片
+  cached_network_image: ^3.4.1
+
   #android日志打印
   atmob_logging:
     version: ^0.0.5