Browse Source

[feat]完成我的,关于我们,反馈建议的页面

云天逵 8 months ago
parent
commit
b90a3a71a5
67 changed files with 1675 additions and 673 deletions
  1. 2 1
      android/app/src/main/AndroidManifest.xml
  2. 25 0
      android/app/src/main/kotlin/com/atmob/keyboard/AtmobApplication.kt
  3. 14 0
      android/app/src/main/res/layout/floating_button_layout.xml
  4. 57 0
      android/app/src/main/res/layout/keyboard_layout.xml
  5. 6 0
      android/app/src/main/res/xml/method.xml
  6. 2 1
      android/build.gradle.kts
  7. BIN
      assets/images/bg_character_boy_banner.webp
  8. BIN
      assets/images/bg_character_girl_banner.webp
  9. BIN
      assets/images/bg_mine_vip_card.webp
  10. BIN
      assets/images/icon_about_arrow_left.webp
  11. BIN
      assets/images/icon_black_back.webp
  12. BIN
      assets/images/icon_mine_keyboard_setting.webp
  13. BIN
      assets/images/icon_mine_order_logo.webp
  14. 0 0
      assets/images/icon_mine_personal_profile.webp
  15. BIN
      assets/images/icon_mine_preset.webp
  16. BIN
      assets/images/icon_mine_privacy.webp
  17. BIN
      assets/images/icon_mine_report_complaint.webp
  18. BIN
      assets/images/icon_mine_settings.webp
  19. BIN
      assets/images/icon_mine_share.webp
  20. BIN
      assets/images/icon_mine_system_notification.webp
  21. BIN
      assets/images/icon_mine_tutorials.webp
  22. 0 0
      assets/images/icon_tab_character_selected.webp
  23. 0 0
      assets/images/icon_tab_character_unselect.webp
  24. 26 11
      assets/string/base/string.xml
  25. 12 0
      lib/base/base_controller.dart
  26. 1 0
      lib/base/base_request.dart
  27. 19 3
      lib/data/api/atmob_api.dart
  28. 69 2
      lib/data/api/atmob_api.g.dart
  29. 19 0
      lib/data/api/request/complaint_submit_request.dart
  30. 16 0
      lib/data/api/response/character_group_response.dart
  31. 29 0
      lib/data/bean/character_group_info.dart
  32. 2 15
      lib/data/consts/constants.dart
  33. 17 0
      lib/data/consts/web_url.dart
  34. 11 0
      lib/data/repository/account_repository.dart
  35. 25 0
      lib/data/repository/characters_repository.dart
  36. 3 3
      lib/device/platform_android_info.dart
  37. 43 6
      lib/di/get_it.config.dart
  38. 33 13
      lib/di/network_module.dart
  39. 6 4
      lib/main.dart
  40. 32 0
      lib/module/about/about_controller.dart
  41. 165 0
      lib/module/about/about_page.dart
  42. 63 0
      lib/module/browser/browser_controller.dart
  43. 65 0
      lib/module/browser/browser_page.dart
  44. 33 0
      lib/module/character/character_controller.dart
  45. 16 0
      lib/module/character/character_view.dart
  46. 56 0
      lib/module/feedback/feedback_controller.dart
  47. 174 0
      lib/module/feedback/feedback_page.dart
  48. 0 5
      lib/module/home/home_controller.dart
  49. 0 18
      lib/module/home/home_view.dart
  50. 0 53
      lib/module/keyboard_setting/keyboard_setting_controller.dart
  51. 0 126
      lib/module/keyboard_setting/keyboard_setting_page.dart
  52. 19 0
      lib/module/login/login_controller.dart
  53. 13 0
      lib/module/login/login_page.dart
  54. 12 5
      lib/module/main/main_controller.dart
  55. 34 27
      lib/module/main/main_page.dart
  56. 42 34
      lib/module/mine/mine_controller.dart
  57. 85 191
      lib/module/mine/mine_view.dart
  58. 135 0
      lib/plugins/keyboard_android_platform.dart
  59. 36 51
      lib/resource/assets.gen.dart
  60. 0 82
      lib/resource/string.gen.dart
  61. 23 7
      lib/router/app_pages.dart
  62. 25 0
      lib/utils/error_handler.dart
  63. 1 1
      lib/utils/privacy_compliance.dart
  64. 142 0
      lib/utils/sse_parse_util.dart
  65. 52 0
      lib/widget/commonAppBar.dart
  66. 7 9
      pubspec.lock
  67. 8 5
      pubspec.yaml

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

@@ -1,7 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
     <application
+        android:allowBackup="false"
         android:label="keyboard"
-        android:name="${applicationName}"
+        android:name=".AtmobApplication"
         android:icon="@mipmap/ic_launcher">
         <activity
             android:name=".MainActivity"

+ 25 - 0
android/app/src/main/kotlin/com/atmob/keyboard/AtmobApplication.kt

@@ -0,0 +1,25 @@
+package com.atmob.keyboard
+
+import android.app.Application
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.embedding.engine.FlutterEngineCache
+import io.flutter.embedding.engine.dart.DartExecutor
+
+class AtmobApplication : Application() {
+
+    // 声明一个 FlutterEngine 实例
+    lateinit var flutterEngine: FlutterEngine
+
+    override fun onCreate() {
+        super.onCreate()
+
+        // 实例化 FlutterEngine,并确保应用退出时,FlutterEngine 能继续运行
+        flutterEngine = FlutterEngine(this).apply {
+            // 设置 Dart 入口点,确保 Dart 代码能够执行
+            dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
+        }
+
+        // 将 FlutterEngine 添加到 FlutterEngineCache,使用指定的 engine ID 进行管理
+        FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine)
+    }
+}

+ 14 - 0
android/app/src/main/res/layout/floating_button_layout.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:id="@+id/floating_button"
+        android:layout_width="60dp"
+        android:layout_height="60dp"
+        android:src="@android:drawable/ic_menu_manage"
+        android:background="@android:color/holo_blue_light"
+        android:scaleType="centerInside"
+        android:padding="10dp"/>
+</FrameLayout>

+ 57 - 0
android/app/src/main/res/layout/keyboard_layout.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@android:color/holo_green_light"
+    android:orientation="vertical"
+    android:padding="5dp">
+
+    <!-- 输入框 -->
+    <EditText
+        android:id="@+id/keyboard_input"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:cursorVisible="false"
+        android:focusable="false"
+        android:hint="复制过来的文本"
+        android:inputType="none"
+        android:longClickable="true"
+        android:textSize="18sp" />
+
+    <!-- 数字键盘 + 操作按钮 -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:weightSum="4">
+
+        <GridLayout
+            android:id="@+id/number_pad"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3"
+            android:columnCount="3"
+            android:rowCount="4"
+            android:padding="5dp">
+
+            <!-- 这里原有的静态按钮定义已全部删除 -->
+
+        </GridLayout>
+
+        <!-- 操作按钮 -->
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical">
+
+            <Button android:id="@+id/btn_paste" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="粘贴"/>
+            <Button android:id="@+id/btn_delete" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="删除"/>
+            <Button android:id="@+id/btn_clear" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="清空"/>
+            <Button android:id="@+id/btn_send" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="发送"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>

+ 6 - 0
android/app/src/main/res/xml/method.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity=".MainActivity"
+    android:supportsSwitchingToNextInputMethod="true">
+
+</input-method>

+ 2 - 1
android/build.gradle.kts

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

BIN
assets/images/bg_character_boy_banner.webp


BIN
assets/images/bg_character_girl_banner.webp


BIN
assets/images/bg_mine_vip_card.webp


BIN
assets/images/icon_about_arrow_left.webp


BIN
assets/images/icon_black_back.webp


BIN
assets/images/icon_mine_keyboard_setting.webp


BIN
assets/images/icon_mine_order_logo.webp


assets/images/icon_mine_user_agreement.webp → assets/images/icon_mine_personal_profile.webp


BIN
assets/images/icon_mine_preset.webp


BIN
assets/images/icon_mine_privacy.webp


BIN
assets/images/icon_mine_report_complaint.webp


BIN
assets/images/icon_mine_settings.webp


BIN
assets/images/icon_mine_share.webp


BIN
assets/images/icon_mine_system_notification.webp


BIN
assets/images/icon_mine_tutorials.webp


assets/images/icon_tab_home_selected.webp → assets/images/icon_tab_character_selected.webp


assets/images/icon_tab_home_unselect.webp → assets/images/icon_tab_character_unselect.webp


+ 26 - 11
assets/string/base/string.xml

@@ -3,21 +3,17 @@
     <string name="app_name">追爱小键盘</string>
     <string name="main_tab_keyboard">键盘</string>
     <string name="main_tab_mine">我的</string>
-    <string name="main_tab_home">人设</string>
+    <string name="main_tab_character">人设</string>
 
 
-
-    <string name="preset">基础预设</string>
-    <string name="keyboard_setting">键盘设置</string>
+    <!--我的页面-->
     <string name="online_customer_service">在线客服</string>
-    <string name="setting">设置</string>
-    <string name="user_agreement">用户协议</string>
-    <string name="privacy_policy">隐私政策</string>
+    <string name="tutorials">使用教程</string>
+    <string name="personal_profile">个人档案</string>
     <string name="feedback">意见反馈</string>
-    <string name="system_notification">系统消息</string>
     <string name="about_us">关于我们</string>
-    <string name="share_app">分享应用</string>
-    <string name="report_complaint">投诉举报</string>
+    <string name="mine_account_logged_desc">用户</string>
+    <string name="mine_account_no_login">游客,去登陆</string>
 
 
     <string name="direct_send">生成内容直接发送</string>
@@ -36,8 +32,8 @@
     <string name="vip_level1_btn">立即续费</string>
     <string name="vip_level2_btn">立即查看</string>
 
+    <string name="network_error">网络异常</string>
 
-    <string name="my_orders">我的订单</string>
 
     <string name="account_no_login">账号未登录</string>
     <string name="login_account">登录账号</string>
@@ -46,5 +42,24 @@
     <string name="login_success">登录成功</string>
     <string name="login_too_often_toast">登录过于频繁,请稍后再试</string>
     <string name="login_failed_toast">登录失败</string>
+    <string name="login_verification_code_error_toast">验证码输入错误,请重新输入</string>
+
+
+    <!-- 反馈页面-->
+    <string name="feedback_content_title">问题描述</string>
+    <string name="feedback_content_hint">请描述您的问题或建议,或者您可以联系我们在线客服</string>
+    <string name="feedback_phone">联系电话</string>
+    <string name="feedback_phone_hint">填写手机号,方便更快为你解决问题</string>
+    <string name="feedback_submit">提交</string>
+    <string name="feedback_content_empty">请输入反馈内容</string>
+    <string name="feedback_phone_empty">请输入联系电话</string>
+    <string name="feedback_submit_success">感谢您的意见与反馈</string>
+
+    <!-- 关于我们页面-->
+    <string name="current_version">当前版本</string>
+
+    <string name="privacy_policy">隐私政策</string>
+    <string name="service_terms">服务条款</string>
+    <string name="child_privacy_policy">儿童隐私保护协议</string>
 
 </resources>

+ 12 - 0
lib/base/base_controller.dart

@@ -1,5 +1,6 @@
 import 'package:flutter/cupertino.dart';
 import 'package:get/get.dart';
+import 'package:get/get_connect/sockets/src/socket_notifier.dart';
 
 class BaseController extends GetxController {
   Map? parameters;
@@ -11,6 +12,17 @@ class BaseController extends GetxController {
     _initParameters();
   }
 
+  @override
+  void onReady() {
+    debugPrint('BaseController $runtimeType onReady');
+    super.onReady();
+  }
+
+  @override
+  void onClose() {
+    debugPrint('BaseController $runtimeType onClose');
+    super.onReady();
+  }
   void _initParameters() {
     var getParameters = Get.parameters;
     var getArguments = Get.arguments;

+ 1 - 0
lib/base/base_request.dart

@@ -112,6 +112,7 @@ class BaseRequest {
     packageName = appInfoUtil.packageName;
     appVersionName = appInfoUtil.appVersionName;
     appVersionCode = appInfoUtil.appVersionCode;
+    debugPrint("packageName: $packageName, appVersionName: $appVersionName, appVersionCode: $appVersionCode");
   }
 
   void initChannelInfo() {

+ 19 - 3
lib/data/api/atmob_api.dart

@@ -1,13 +1,14 @@
 import 'package:dio/dio.dart';
 import 'package:keyboard/base/base_response.dart';
+import 'package:keyboard/data/api/request/complaint_submit_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/response/character_group_response.dart';
 import 'package:keyboard/data/api/response/login_response.dart';
 import 'package:retrofit/error_logger.dart';
 import 'package:retrofit/http.dart';
 
-import '../consts/constants.dart';
-import '../../di/network_module.dart';
+import '../../base/app_base_request.dart';
 
 part 'atmob_api.g.dart';
 
@@ -15,14 +16,29 @@ part 'atmob_api.g.dart';
 abstract class AtmobApi {
   factory AtmobApi(Dio dio, {String baseUrl}) = _AtmobApi;
 
+  // 获取手机验证码
   @POST("/central/open/v1/user/code")
   Future<BaseResponse> loginSendCode(@Body() SendCodeRequest request);
 
+  // 登录
   @POST("/central/open/v1/user/login")
   Future<BaseResponse<LoginResponse>> loginUserLogin(
     @Body() LoginRequest request,
   );
 
+  // 注销账号
   @POST("/central/open/v1/user/deprecate")
-  Future<BaseResponse> logoutUser();
+  Future<BaseResponse> logoutUser(@Body() AppBaseRequest request);
+
+  // 意见反馈
+  @POST("/project/keyboard/v1/complaint/submit")
+  Future<BaseResponse> complaintSubmit(@Body() ComplaintSubmitRequest request);
+
+  // 获取人设主题
+  @POST("/project/keyboard/v1/character/group")
+  Future<BaseResponse<CharacterGroupResponse>> getCharactersGroup(
+    @Body() AppBaseRequest request,
+  );
+
+
 }

+ 69 - 2
lib/data/api/atmob_api.g.dart

@@ -82,11 +82,12 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
-  Future<BaseResponse<dynamic>> logoutUser() async {
+  Future<BaseResponse<dynamic>> logoutUser(AppBaseRequest request) async {
     final _extra = <String, dynamic>{};
     final queryParameters = <String, dynamic>{};
     final _headers = <String, dynamic>{};
-    const Map<String, dynamic>? _data = null;
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
     final _options = _setStreamType<BaseResponse<dynamic>>(
       Options(method: 'POST', headers: _headers, extra: _extra)
           .compose(
@@ -111,6 +112,72 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<dynamic>> complaintSubmit(
+    ComplaintSubmitRequest 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/complaint/submit',
+            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<CharacterGroupResponse>> getCharactersGroup(
+    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<CharacterGroupResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/character/group',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<CharacterGroupResponse> _value;
+    try {
+      _value = BaseResponse<CharacterGroupResponse>.fromJson(
+        _result.data!,
+        (json) => CharacterGroupResponse.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 ||

+ 19 - 0
lib/data/api/request/complaint_submit_request.dart

@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'complaint_submit_request.g.dart';
+
+@JsonSerializable()
+class ComplaintSubmitRequest extends AppBaseRequest {
+  @JsonKey(name: "phone")
+  String? phone;
+
+  @JsonKey(name: "content")
+  String content;
+
+  ComplaintSubmitRequest(this.phone, this.content);
+
+  @override
+  Map<String, dynamic> toJson() => _$ComplaintSubmitRequestToJson(this);
+}

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

@@ -0,0 +1,16 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/character_group_info.dart';
+
+part 'character_group_response.g.dart';
+
+@JsonSerializable()
+class CharacterGroupResponse {
+  @JsonKey(name: "characterGroups")
+  late final List<CharacterGroupInfo> characterGroups;
+
+  CharacterGroupResponse({required this.characterGroups});
+
+  factory CharacterGroupResponse.fromJson(Map<String, dynamic> json) =>
+      _$CharacterGroupResponseFromJson(json);
+}

+ 29 - 0
lib/data/bean/character_group_info.dart

@@ -0,0 +1,29 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'character_group_info.g.dart';
+
+@JsonSerializable()
+class CharacterGroupInfo {
+  //主题id
+  @JsonKey(name: 'id')
+  late String id;
+
+  //主题名称
+  @JsonKey(name: 'name')
+  late String name;
+
+  //icon
+  @JsonKey(name: 'iconUrl')
+  late String iconUrl;
+
+  CharacterGroupInfo({
+    required this.id,
+    required this.name,
+    required this.iconUrl,
+  });
+
+  factory CharacterGroupInfo.fromJson(Map<String, dynamic> json) =>
+      _$CharacterGroupInfoFromJson(json);
+
+  Map<String, dynamic> toJson() => _$CharacterGroupInfoToJson(this);
+}

+ 2 - 15
lib/data/consts/constants.dart

@@ -6,7 +6,7 @@ import '../../utils/mmkv_util.dart';
 class Constants {
   Constants._();
 
-  static const String env = envProd;
+  static const String env = envDev;
 
   static const String envDev = 'dev';
 
@@ -14,26 +14,13 @@ class Constants {
 
   static const String envProd = 'prod';
 
-  static const String _devBaseUrl = "http://192.168.10.230:8880";
+  static const String _devBaseUrl = "http://192.168.10.113:8880";
 
   static const String _testBaseUrl = "http://42.193.245.11";
 
   static const String _prodBaseUrl = "https://project-api.atmob.com";
 
-  static const String privacyPolicy =
-      "https://doc.v8dashen.com/doc/298eb75d38dc2c4a";
 
-  static const String userAgreement =
-      "https://doc.v8dashen.com/doc/417838a4f155ec74";
-
-  static const String personalInformationList =
-      "https://doc.v8dashen.com/doc/d82db8fc7ac57d7f";
-
-  static const String thirdPartyList =
-      "https://doc.v8dashen.com/doc/626cfc364a1358a9";
-
-  static const String kidPolicy =
-      "https://doc.v8dashen.com/doc/0bc7b973c78011c9";
 
   static String baseUrl = getBaseUrl();
 

+ 17 - 0
lib/data/consts/web_url.dart

@@ -0,0 +1,17 @@
+class WebUrl {
+  WebUrl._();
+
+  static const String _privacyPolicy =
+      "https://doc.v8dashen.com/doc/298eb75d38dc2c4a";
+
+  static const String _serviceAgreement =
+      "https://doc.v8dashen.com/doc/298eb75d38dc2c4a";
+  static const String _kidPolicy =
+      "https://doc.v8dashen.com/doc/298eb75d38dc2c4a";
+
+  static String get privacyPolicy => _privacyPolicy;
+
+  static String get kidPolicy => _kidPolicy;
+
+  static String get serviceAgreement => _serviceAgreement;
+}

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

@@ -6,6 +6,7 @@ import '../../di/get_it.dart';
 import '../../utils/atmob_log.dart';
 import '../../utils/http_handler.dart';
 import '../../utils/mmkv_util.dart';
+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';
@@ -76,6 +77,9 @@ class AccountRepository {
   }
 
   void onLoginSuccess(String phoneNum, String authToken) {
+    KVUtil.putString(keyAccountLoginPhoneNum, null);
+    KVUtil.putString(keyAccountLoginToken, null);
+
     AccountRepository.token = authToken;
     loginPhoneNum.value = phoneNum;
 
@@ -92,6 +96,13 @@ class AccountRepository {
     loginPhoneNum.value = null;
   }
 
+  // 意见反馈
+  Future<void> complaintSubmit(String? phone, String content) {
+    return atmobApi
+        .complaintSubmit(ComplaintSubmitRequest(phone, content))
+        .then(HttpHandler.handle(true));
+  }
+
   static AccountRepository getInstance() {
     return getIt.get<AccountRepository>();
   }

+ 25 - 0
lib/data/repository/characters_repository.dart

@@ -0,0 +1,25 @@
+import 'package:injectable/injectable.dart';
+
+import '../../base/app_base_request.dart';
+import '../../utils/atmob_log.dart';
+import '../../utils/http_handler.dart';
+import '../api/atmob_api.dart';
+import '../api/response/character_group_response.dart';
+
+@lazySingleton
+class CharactersRepository {
+  final AtmobApi atmobApi;
+  final String tag = "CharactersRepository";
+
+  CharactersRepository(this.atmobApi) {
+    AtmobLog.d(tag, '$tag....init');
+  }
+
+
+
+  Future<CharacterGroupResponse> getCharactersGroup() {
+    return atmobApi
+        .getCharactersGroup(AppBaseRequest())
+        .then(HttpHandler.handle(true));
+  }
+}

+ 3 - 3
lib/device/platform_android_info.dart

@@ -2,7 +2,7 @@ import 'dart:io';
 
 import 'package:device_info_plus/device_info_plus.dart';
 import 'package:android_id/android_id.dart';
-import 'package:oaid/oaid_kit.dart';
+// import 'package:oaid/oaid_kit.dart';
 import 'atmob_platform_info.dart';
 
 class PlatformAndroidInfo {
@@ -15,8 +15,8 @@ class PlatformAndroidInfo {
           .setAndroidId(deviceId)
           .setBrand(androidInfo.brand)
           .setModel(androidInfo.model);
-      String? oaid = await Oaid.getOaid();
-      atmobPlatformInfo.setOaid(oaid);
+      // String? oaid = await Oaid.getOaid();
+      // atmobPlatformInfo.setOaid(oaid);
     }
   }
 }

+ 43 - 6
lib/di/get_it.config.dart

@@ -14,10 +14,18 @@ import 'package:get_it/get_it.dart' as _i174;
 import 'package:injectable/injectable.dart' as _i526;
 
 import '../data/api/atmob_api.dart' as _i243;
+import '../data/api/atmob_stream_api.dart' as _i329;
 import '../data/repository/account_repository.dart' as _i83;
-import '../module/keyboard_setting/keyboard_setting_controller.dart' as _i235;
+import '../data/repository/characters_repository.dart' as _i421;
+import '../data/repository/chat_repository.dart' as _i425;
+import '../module/about/about_controller.dart' as _i256;
+import '../module/browser/browser_controller.dart' as _i923;
+import '../module/character/character_controller.dart' as _i888;
+import '../module/feedback/feedback_controller.dart' as _i876;
+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 '../plugins/keyboard_android_platform.dart' as _i79;
 import 'network_module.dart' as _i567;
 
 extension GetItInjectableX on _i174.GetIt {
@@ -28,20 +36,49 @@ extension GetItInjectableX on _i174.GetIt {
   }) {
     final gh = _i526.GetItHelper(this, environment, environmentFilter);
     final networkModule = _$NetworkModule();
-    gh.factory<_i235.KeyboardSettingController>(
-      () => _i235.KeyboardSettingController(),
-    );
+    gh.factory<_i256.AboutController>(() => _i256.AboutController());
+    gh.factory<_i923.BrowserController>(() => _i923.BrowserController());
+    gh.factory<_i1008.LoginController>(() => _i1008.LoginController());
     gh.factory<_i731.MainController>(() => _i731.MainController());
-    gh.singleton<_i361.Dio>(() => networkModule.createDefaultDio());
+    gh.singleton<_i361.Dio>(
+      () => networkModule.createStreamDio(),
+      instanceName: 'streamDio',
+    );
+    gh.singleton<_i361.Dio>(
+      () => networkModule.createDefaultDio(),
+      instanceName: 'defaultDio',
+    );
+    gh.singleton<_i329.AtmobStreamApi>(
+      () => networkModule.provideAtmobStreamApi(
+        gh<_i361.Dio>(instanceName: 'streamDio'),
+      ),
+    );
+    gh.lazySingleton<_i425.ChatRepository>(
+      () => _i425.ChatRepository(gh<_i329.AtmobStreamApi>()),
+    );
     gh.singleton<_i243.AtmobApi>(
-      () => networkModule.provideAtmobApi(gh<_i361.Dio>()),
+      () => networkModule.provideAtmobApi(
+        gh<_i361.Dio>(instanceName: 'defaultDio'),
+      ),
+    );
+    gh.lazySingleton<_i79.KeyboardAndroidPlatform>(
+      () => _i79.KeyboardAndroidPlatform(gh<_i425.ChatRepository>()),
     );
     gh.lazySingleton<_i83.AccountRepository>(
       () => _i83.AccountRepository(gh<_i243.AtmobApi>()),
     );
+    gh.lazySingleton<_i421.CharactersRepository>(
+      () => _i421.CharactersRepository(gh<_i243.AtmobApi>()),
+    );
+    gh.factory<_i876.FeedbackController>(
+      () => _i876.FeedbackController(gh<_i83.AccountRepository>()),
+    );
     gh.factory<_i732.MineController>(
       () => _i732.MineController(gh<_i83.AccountRepository>()),
     );
+    gh.factory<_i888.CharacterController>(
+      () => _i888.CharacterController(gh<_i421.CharactersRepository>()),
+    );
     return this;
   }
 }

+ 33 - 13
lib/di/network_module.dart

@@ -3,6 +3,7 @@ import 'package:injectable/injectable.dart';
 import 'package:keyboard/data/api/atmob_api.dart';
 import 'package:pretty_dio_logger/pretty_dio_logger.dart';
 
+import '../data/api/atmob_stream_api.dart';
 import '../data/consts/constants.dart';
 import '../utils/stream_dio_log_interceptor.dart';
 import '../data/consts/build_config.dart';
@@ -10,27 +11,46 @@ import '../data/consts/build_config.dart';
 @module
 abstract class NetworkModule {
   @singleton
+  @Named("defaultDio")
   Dio createDefaultDio() {
     Dio dio = Dio(BaseOptions());
-    dio.interceptors.add(PrettyDioLogger(
-      requestHeader: true,
-      requestBody: true,
-      responseBody: true,
-      responseHeader: false,
-      error: true,
-      compact: true,
-      enabled: BuildConfig.isDebug,
-    ));
+    dio.interceptors.add(
+      PrettyDioLogger(
+        requestHeader: true,
+        requestBody: true,
+        responseBody: true,
+        responseHeader: false,
+        error: true,
+        compact: true,
+        enabled: BuildConfig.isDebug,
+      ),
+    );
     return dio;
   }
 
+  @singleton
+  @Named("streamDio")
+  Dio createStreamDio() {
+    Dio streamDio = Dio(
+      BaseOptions(
+        responseType: ResponseType.stream,
+        headers: {
+          'Content-Type': 'application/json',
+          'Accept': 'text/event-stream, application/json',
+        },
+      ),
+    );
+    streamDio.interceptors.add(StreamDioLogInterceptor());
+    return streamDio;
+  }
 
   @singleton
-  AtmobApi provideAtmobApi(Dio dio) {
+  AtmobApi provideAtmobApi(@Named("defaultDio")Dio dio) {
     return AtmobApi(dio, baseUrl: Constants.baseUrl);
   }
 
-
-
+  @singleton
+  AtmobStreamApi provideAtmobStreamApi(@Named("streamDio")Dio streamDio) {
+    return AtmobStreamApi(streamDio, baseUrl: Constants.baseUrl);
+  }
 }
-

+ 6 - 4
lib/main.dart

@@ -1,12 +1,12 @@
 import 'dart:io';
 
-import 'package:atmob_channel_reader/atmob_channel_reader.dart';
 import 'package:atmob_logging/atmob_logging.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
+import 'package:keyboard/plugins/keyboard_android_platform.dart';
 import 'package:keyboard/resource/colors.gen.dart';
 import 'package:keyboard/resource/string.gen.dart';
 import 'package:keyboard/resource/string_source.dart';
@@ -20,11 +20,9 @@ import 'package:keyboard/utils/toast_util.dart';
 
 import 'data/consts/build_config.dart';
 import 'data/consts/constants.dart';
-import 'device/atmob_platform_info.dart';
 import 'device/device_info_util.dart';
 import 'di/get_it.dart';
 
-
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
 
@@ -35,6 +33,11 @@ void main() async {
   //隐私相关:系统参数&第三方sdk初始化
   await PrivacyCompliance.ensurePolicyGranted(AppInitTask());
 
+  if (Platform.isAndroid) {
+    //键盘
+    KeyboardAndroidPlatform.getInstance();
+  }
+
   runApp(const MyApp());
   //檢查地址
   checkEnv();
@@ -79,7 +82,6 @@ void smartConfig() {
   );
 }
 
-
 class MyApp extends StatelessWidget {
   const MyApp({super.key});
 

+ 32 - 0
lib/module/about/about_controller.dart

@@ -0,0 +1,32 @@
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+
+import '../../base/base_controller.dart';
+import '../../data/consts/web_url.dart';
+import '../browser/browser_page.dart';
+
+@injectable
+class AboutController extends BaseController {
+  AboutController();
+
+  clickBack() {
+    debugPrint('clickBack');
+    Get.back();
+  }
+
+  clickPrivacyPolicy() {
+    debugPrint('clickPrivacyPolicy');
+    BrowserPage.start(WebUrl.privacyPolicy);
+  }
+
+  clickServiceAgreement() {
+    debugPrint('clickServiceAgreement');
+    BrowserPage.start(WebUrl.serviceAgreement);
+  }
+
+  clickChildPrivacyPolicy() {
+    debugPrint('clickChildPrivacyPolicy');
+    BrowserPage.start(WebUrl.kidPolicy);
+  }
+}

+ 165 - 0
lib/module/about/about_page.dart

@@ -0,0 +1,165 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/resource/string.gen.dart';
+
+import '../../base/base_page.dart';
+import '../../resource/assets.gen.dart';
+import '../../router/app_pages.dart';
+import '../../utils/app_info_util.dart';
+import '../../widget/commonAppBar.dart';
+import 'about_controller.dart';
+import 'package:get/get.dart';
+
+class AboutPage extends BasePage<AboutController> {
+  const AboutPage({super.key});
+
+  static start() {
+    Get.toNamed(RoutePath.about);
+  }
+
+  @override
+  Color backgroundColor() {
+    return const Color(0xFFF6F5FA);
+  }
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        Scaffold(
+          appBar: CommonAppBar(
+            title: StringName.aboutUs,
+            backgroundColor: backgroundColor,
+            onBack: () {
+              controller.clickBack();
+            },
+          ),
+          body: Container(
+            color: backgroundColor(),
+            child: Column(
+              children: [
+                Container(
+                  margin: EdgeInsets.only(top: 40.w),
+                  child: Column(
+                    children: [
+                      // icon
+                      Container(
+                        width: 72.w,
+                        height: 72.w,
+                        decoration: BoxDecoration(
+                          color: Colors.grey[200],
+                          borderRadius: BorderRadius.circular(16),
+                        ),
+                      ),
+                      SizedBox(height: 21.h),
+                      Text(
+                        StringName.appName,
+                        style: TextStyle(
+                          color: Colors.black.withAlpha(204),
+                          fontSize: 26.sp,
+                          fontWeight: FontWeight.w500,
+                        ),
+                      ),
+                      SizedBox(height: 6.h),
+                      // 版本号
+                      Text(
+                        '${StringName.currentVersion} ${appInfoUtil.appVersionName}',
+                        style: TextStyle(
+                          fontSize: 12.sp,
+                          color: Colors.black.withAlpha(102),
+                          fontWeight: FontWeight.w400,
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+
+                Container(
+                  margin: EdgeInsets.symmetric(
+                    horizontal: 16.w,
+                    vertical: 28.h,
+                  ),
+                  padding: EdgeInsets.symmetric(vertical: 8.h),
+                  decoration: BoxDecoration(
+                    color: Colors.white,
+                    borderRadius: BorderRadius.circular(12.r),
+                  ),
+                  child: Column(
+                    children: [
+                      _buildListItem(
+                        StringName.privacyPolicy,
+                        onTap: controller.clickPrivacyPolicy,
+                      ),
+                      _buildDivider(),
+                      _buildListItem(
+                        StringName.serviceTerms,
+                        onTap: controller.clickServiceAgreement,
+                      ),
+                      _buildDivider(),
+                      _buildListItem(
+                        StringName.childPrivacyPolicy,
+                        onTap: controller.clickChildPrivacyPolicy,
+                      ),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ),
+        IgnorePointer(
+          child: Assets.images.bgMine.image(
+            width: 360.w,
+            height: 206.h,
+            fit: BoxFit.fill,
+          ),
+        ),
+      ],
+    );
+  }
+
+  Widget _buildListItem(String title, {required VoidCallback onTap}) {
+    return InkWell(
+      // 效果
+      onTap: onTap,
+      child: Container(
+        padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h),
+        child: Row(
+          children: [
+            Text(
+              title,
+              style: TextStyle(
+                fontSize: 14.sp,
+                color: Colors.black.withAlpha(204),
+              ),
+            ),
+            const Spacer(),
+            Assets.images.iconAboutArrowLeft.image(width: 20.w, height: 20.w),
+          ],
+        ),
+      ),
+    );
+  }
+
+  //   列表的线
+  Widget _buildDivider() {
+    return Container(
+      margin: EdgeInsets.symmetric(horizontal: 20.w),
+      decoration: ShapeDecoration(
+        shape: RoundedRectangleBorder(
+          side: BorderSide(
+            width: 1,
+            strokeAlign: BorderSide.strokeAlignCenter,
+            color: Color(0xFFF5F4F9),
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 63 - 0
lib/module/browser/browser_controller.dart

@@ -0,0 +1,63 @@
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+
+import '../../base/base_controller.dart';
+import '../../resource/colors.gen.dart';
+
+@injectable
+class BrowserController extends BaseController {
+  String url = (Get.arguments is String) ? (Get.arguments as String) : '';
+
+  final WebViewController webViewController = WebViewController();
+
+  final title = ''.obs;
+
+  @override
+  void onInit() {
+    super.onInit();
+    _initWebSetting();
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    _loadUrl();
+  }
+
+  void _initWebSetting() {
+    webViewController.setJavaScriptMode(JavaScriptMode.unrestricted);
+    webViewController.setBackgroundColor(ColorName.white);
+    webViewController.setNavigationDelegate(
+      NavigationDelegate(
+        onPageFinished: (String url) {
+          _getTitle();
+        },
+      ),
+    );
+  }
+
+  void _loadUrl() {
+    if (url.isEmpty) {
+      return;
+    }
+    webViewController.loadRequest(Uri.parse(url));
+  }
+
+  Future<bool> handleBack() async {
+    if (await webViewController.canGoBack()) {
+      webViewController.goBack();
+      return false;
+    }
+    return true;
+  }
+
+  void _getTitle() async {
+    await Future.delayed(const Duration(milliseconds: 500));
+    webViewController.getTitle().then((title) {
+      if (title != null) {
+        this.title.value = title;
+      }
+    });
+  }
+}

+ 65 - 0
lib/module/browser/browser_page.dart

@@ -0,0 +1,65 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+
+import '../../base/base_page.dart';
+import '../../resource/assets.gen.dart';
+import '../../resource/colors.gen.dart';
+import '../../router/app_pages.dart';
+import 'browser_controller.dart';
+
+class BrowserPage extends BasePage<BrowserController> {
+  const BrowserPage({super.key});
+
+  static start(String url) {
+    Get.toNamed(RoutePath.browser, arguments: url);
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return PopScope(
+      canPop: true,
+
+      onPopInvokedWithResult: (didPop, result) async {
+        if (didPop) {
+          return;
+        }
+        await controller.handleBack();
+      },
+      child: Scaffold(
+        backgroundColor: Colors.transparent,
+        appBar: AppBar(
+          systemOverlayStyle: SystemUiOverlayStyle.dark,
+          backgroundColor: Colors.transparent,
+          title: Obx(
+            () => Text(
+              controller.title.value,
+              style: TextStyle(
+                fontSize: 17.sp,
+                color: ColorName.primaryTextColor,
+              ),
+            ),
+          ),
+          leading: IconButton(
+            icon: SizedBox(
+              width: 24.w,
+              height: 24.w,
+              child: Assets.images.iconBlackBack.image(),
+            ),
+            // Custom icon
+            onPressed: () {
+              Get.back();
+            },
+          ),
+        ),
+        body: _buildContentView(),
+      ),
+    );
+  }
+
+  Widget _buildContentView() {
+    return WebViewWidget(controller: controller.webViewController);
+  }
+}

+ 33 - 0
lib/module/character/character_controller.dart

@@ -0,0 +1,33 @@
+import 'package:flutter/cupertino.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:keyboard/data/api/response/character_group_response.dart';
+
+import '../../data/repository/characters_repository.dart';
+
+@injectable
+class CharacterController extends BaseController {
+  final String tag = "CharacterController";
+  final CharactersRepository charactersRepository;
+
+  CharacterController(this.charactersRepository);
+
+  @override
+  void onInit() {
+    super.onInit();
+
+    charactersRepository.getCharactersGroup().then((CharacterGroupResponse value) {
+      debugPrint(value.characterGroups.toString());
+    });
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+  }
+}

+ 16 - 0
lib/module/character/character_view.dart

@@ -0,0 +1,16 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:keyboard/base/base_view.dart';
+
+import 'character_controller.dart';
+
+class CharacterView extends BaseView<CharacterController> {
+  const CharacterView({super.key});
+
+
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Center(child: Text("Character", style: TextStyle(color: Colors.black)));
+  }
+}

+ 56 - 0
lib/module/feedback/feedback_controller.dart

@@ -0,0 +1,56 @@
+import 'package:flutter/cupertino.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:keyboard/data/repository/account_repository.dart';
+import 'package:get/get.dart';
+
+import '../../resource/string.gen.dart';
+import '../../utils/error_handler.dart';
+import '../../utils/toast_util.dart';
+
+@injectable
+class FeedbackController extends BaseController {
+  final RxString _phone = ''.obs;
+  final RxString _content = "".obs;
+
+  get phone => _phone.value;
+
+  get content => _content.value;
+
+  final AccountRepository accountRepository;
+
+  FeedbackController(this.accountRepository);
+
+  onPhoneChanged(String phone) {
+    _phone.value = phone;
+  }
+
+  onContentChanged(String content) {
+    _content.value = content;
+  }
+
+  backClick() {
+    debugPrint("backClick");
+    Get.back();
+  }
+
+  submitClick() {
+    if (_content.value.isEmpty) {
+      ToastUtil.show(StringName.feedbackContentEmpty);
+      return;
+    }
+    if (_phone.value.isEmpty) {
+      ToastUtil.show(StringName.feedbackPhoneEmpty);
+      return;
+    }
+    accountRepository
+        .complaintSubmit(_phone.value, _content.value)
+        .then((data) {
+          ToastUtil.show(StringName.feedbackSubmitSuccess);
+          Get.back();
+        })
+        .catchError((error) {
+          ErrorHandler.toastError(error);
+        });
+  }
+}

+ 174 - 0
lib/module/feedback/feedback_page.dart

@@ -0,0 +1,174 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/module/feedback/feedback_controller.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/resource/colors.gen.dart';
+import 'package:keyboard/widget/commonAppBar.dart';
+
+import '../../resource/assets.gen.dart';
+import '../../resource/string.gen.dart';
+import '../../router/app_pages.dart';
+
+class FeedbackPage extends BasePage<FeedbackController> {
+  const FeedbackPage({super.key});
+
+  static start() {
+    Get.toNamed(RoutePath.feedback);
+  }
+
+  @override
+  bool immersive() {
+    return false;
+  }
+
+  @override
+  Color backgroundColor() => Color(0xFFF6F5FA);
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Scaffold(
+      resizeToAvoidBottomInset: false,
+      backgroundColor: backgroundColor(),
+      appBar: CommonAppBar(
+        title: StringName.feedback,
+        backgroundColor: backgroundColor,
+        onBack: () {
+          controller.backClick();
+        },
+      ),
+      body: Container(
+        margin: EdgeInsets.only(left: 16.w, right: 16.w, top: 20.h),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.stretch,
+          children: [
+            Container(
+              padding: EdgeInsets.all(16.r),
+              // height: 221.w,
+              decoration: BoxDecoration(
+                color: Colors.white,
+                borderRadius: BorderRadius.circular(12.r),
+              ),
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  Container(
+                    decoration: BoxDecoration(
+                      color: Colors.white,
+                      borderRadius: BorderRadius.circular(8.r),
+                    ),
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Text(
+                          StringName.feedbackContentTitle,
+                          style: TextStyle(
+                            color: Colors.black,
+                            fontSize: 14.sp,
+                            fontWeight: FontWeight.w400,
+                          ),
+                        ),
+                        SizedBox(height: 8.h),
+                        SizedBox(
+                          height: 161.h,
+                          child: TextField(
+                            maxLength: 300,
+                            maxLines: null,
+                            expands: true,
+                            textAlignVertical: TextAlignVertical.top,
+                            decoration: InputDecoration(
+                              counterText: "",
+                              hintText: StringName.feedbackContentHint,
+                              hintStyle: TextStyle(
+                                color: Colors.black.withAlpha(66),
+                              ),
+                              border: OutlineInputBorder(
+                                borderRadius: BorderRadius.circular(10.r),
+                                borderSide: BorderSide.none, // 移除边框线
+                              ),
+                              filled: true,
+                              fillColor: const Color(0xFFF5F4F9),
+                            ),
+                            onChanged: (value) {
+                              controller.onContentChanged(value);
+                            },
+                          ),
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ),
+            ),
+
+            SizedBox(height: 8.h),
+            Container(
+              padding: EdgeInsets.all(16.r),
+              decoration: BoxDecoration(
+                color: Colors.white,
+                borderRadius: BorderRadius.circular(12),
+              ),
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  Text(
+                    StringName.feedbackPhone,
+                    style: TextStyle(
+                      fontSize: 16,
+                      color: Colors.black.withAlpha(204),
+                    ),
+                  ),
+                  const SizedBox(height: 8),
+                  SizedBox(
+                    height: 48.h,
+                    child: TextField(
+                      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+                      maxLength: 11,
+                      maxLines: null,
+                      expands: true,
+                      textAlignVertical: TextAlignVertical.top,
+                      decoration: InputDecoration(
+                        counterText: "",
+                        hintText: StringName.feedbackPhoneHint,
+                        hintStyle: TextStyle(color: Colors.black.withAlpha(66)),
+                        border: OutlineInputBorder(
+                          borderRadius: BorderRadius.circular(10.r),
+                          borderSide: BorderSide.none,
+                        ),
+                        filled: true,
+                        fillColor: const Color(0xFFF5F4F9),
+                      ),
+                      onChanged: (value) {
+                        controller.onPhoneChanged(value);
+                      },
+                    ),
+                  ),
+                ],
+              ),
+            ),
+
+            Spacer(),
+            ElevatedButton(
+              onPressed: () {
+                controller.submitClick();
+              },
+              style: ElevatedButton.styleFrom(
+                backgroundColor: Colors.purple,
+                padding: EdgeInsets.symmetric(vertical: 16.r),
+                shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(100.r),
+                ),
+              ),
+              child: Text(
+                StringName.feedbackSubmit,
+                style: TextStyle(fontSize: 16.r, color: Colors.white),
+              ),
+            ),
+            SizedBox(height: 16.h),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 0 - 5
lib/module/home/home_controller.dart

@@ -1,5 +0,0 @@
-import 'package:keyboard/base/base_controller.dart';
-
-class HomeController extends BaseController {
-
-}

+ 0 - 18
lib/module/home/home_view.dart

@@ -1,18 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import 'package:keyboard/base/base_view.dart';
-
-import 'home_controller.dart';
-
-class HomeView extends BaseView<HomeController> {
-  const HomeView({super.key});
-
-
-
-  @override
-  Widget buildBody(BuildContext context) {
-    return Container(
-      child: Center(child: Text("HomeView", style: TextStyle(color: Colors.black))),
-    );
-  }
-}

+ 0 - 53
lib/module/keyboard_setting/keyboard_setting_controller.dart

@@ -1,53 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/rendering.dart';
-import 'package:get/get.dart';
-import 'package:injectable/injectable.dart';
-import 'package:keyboard/base/base_controller.dart';
-
-@injectable
-class KeyboardSettingController extends BaseController {
-  /// 生成内容直接发送
-  var directSend = false.obs;
-
-  /// 打开悬浮窗
-  var enableFloatingWindow = false.obs;
-
-  /// 自动打开浮窗(仅当“打开悬浮窗”开启时才可用)
-  var autoOpenFloatingWindow = false.obs;
-
-  @override
-  void onInit() {
-
-    super.onInit();
-
-  }
-  /// 切换“生成内容直接发送”开关
-  void toggleDirectSend(bool value) {
-    debugPrint('$runtimeType toggleDirectSend');
-    directSend.value = value;
-  }
-
-  /// 切换“打开悬浮窗”开关
-  void toggleEnableFloatingWindow(bool value) {
-    debugPrint('$runtimeType toggleEnableFloatingWindow');
-    enableFloatingWindow.value = value;
-
-    // 如果关闭了“打开悬浮窗”,自动关闭“自动打开浮窗”
-    if (!value) {
-      autoOpenFloatingWindow.value = false;
-    }
-  }
-
-  /// 切换“自动打开浮窗”开关
-  void toggleAutoOpenFloatingWindow(bool value) {
-    debugPrint('$runtimeType toggleAutoOpenFloatingWindow');
-    autoOpenFloatingWindow.value = value;
-  }
-
-  //   返回
-  void backClick() {
-    debugPrint('$runtimeType backClick');
-    Get.back();
-
-  }
-}

+ 0 - 126
lib/module/keyboard_setting/keyboard_setting_page.dart

@@ -1,126 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_screenutil/flutter_screenutil.dart';
-import 'package:get/get.dart';
-import 'package:keyboard/base/base_page.dart';
-import 'package:keyboard/module/keyboard_setting/keyboard_setting_controller.dart';
-import 'package:keyboard/resource/string.gen.dart';
-
-import '../../resource/assets.gen.dart';
-import '../../router/app_pages.dart';
-
-class KeyboardSettingPage extends BasePage<KeyboardSettingController> {
-  const KeyboardSettingPage({super.key});
-
-  static start() {
-    Get.toNamed(RoutePath.keyboardSetting);
-  }
-
-  @override
-  Color backgroundColor() {
-    return Color(0xFFF6F5FA);
-  }
-
-  @override
-  bool immersive() => false;
-
-  @override
-  Widget buildBody(BuildContext context) {
-    return Scaffold(
-      backgroundColor: Colors.transparent,
-      appBar: AppBar(
-        backgroundColor: Colors.transparent,
-        // 16.w间距+24.w图标宽度
-        leadingWidth: 40.w,
-        leading: Padding(
-          padding: EdgeInsets.only(left: 16.w),
-          child: GestureDetector(
-            onTap: () {
-              controller.backClick();
-            },
-            child: Assets.images.iconMineBackArrow.image(
-              width: 24.w,
-              height: 24.h,
-            ),
-          ),
-        ),
-        centerTitle: true,
-        title: Text(
-          StringName.keyboardSetting,
-          textAlign: TextAlign.center,
-          style: TextStyle(
-            color: Colors.black,
-            fontSize: 17.sp,
-            fontWeight: FontWeight.w500,
-            height: 1.18.h,
-          ),
-        ),
-      ),
-      body: Container(
-        color: Colors.white,
-        margin: EdgeInsets.symmetric(horizontal: 16.w),
-        padding: EdgeInsets.symmetric(horizontal: 16.w),
-        child: Column(
-          crossAxisAlignment: CrossAxisAlignment.start,
-          children: [
-            _buildSwitchTile(
-              title: StringName.directSend,
-              desc: StringName.directSendDesc,
-              value: controller.directSend,
-              onChanged: (value) {
-                controller.toggleDirectSend(value);
-              },
-            ),
-            _buildSwitchTile(
-              title: StringName.openFloat,
-              desc: StringName.openFloatDesc,
-              value: controller.enableFloatingWindow,
-              onChanged: (value) {
-                controller.enableFloatingWindow.value = value;
-                if (!value) {
-                  controller.autoOpenFloatingWindow.value = false;
-                }
-              },
-            ),
-            Obx(
-              () => _buildSwitchTile(
-                title: StringName.autoOpenFloat,
-                desc: StringName.autoOpenFloatDesc,
-                value: controller.autoOpenFloatingWindow,
-                enabled: controller.enableFloatingWindow.value,
-                onChanged: (value) {
-                  controller.autoOpenFloatingWindow.value = value;
-                },
-              ),
-            ),
-
-
-          ],
-        ),
-      ),
-    );
-  }
-
-  /// 构建带有开关的列表项
-  Widget _buildSwitchTile({
-    required String title,
-    required String desc,
-    required RxBool value,
-    required Function(bool) onChanged, // 切换事件
-    bool enabled = true, // 是否可用
-  }) {
-    return Obx(
-      () => Opacity(
-        opacity: enabled ? 1.0 : 0.5, // 如果不可用,则降低透明度
-        child: ListTile(
-          contentPadding: EdgeInsets.only(left: 16.w), // 左边距
-          title: Text(title),
-          subtitle: Text(desc),
-          trailing: Switch(
-            value: value.value,
-            onChanged: enabled ? onChanged : null, // 如果不可用,则禁用开关
-          ),
-        ),
-      ),
-    );
-  }
-}

+ 19 - 0
lib/module/login/login_controller.dart

@@ -0,0 +1,19 @@
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:get/get.dart';
+@injectable
+class LoginController extends BaseController{
+
+  final RxString _phone = ''.obs;
+  final RxString _code = ''.obs;
+
+  String get phone => _phone.value;
+
+  String get code => _code.value;
+
+
+  @override
+  void onInit() {
+    super.onInit();
+  }
+}

+ 13 - 0
lib/module/login/login_page.dart

@@ -0,0 +1,13 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/module/login/login_controller.dart';
+
+class LoginPage extends BasePage<LoginController> {
+  const LoginPage({super.key});
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Container();
+  }
+}

+ 12 - 5
lib/module/main/main_controller.dart

@@ -1,11 +1,13 @@
 import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
 import 'package:get/get.dart';
 import 'package:injectable/injectable.dart';
 
 import '../../base/base_controller.dart';
 import '../../resource/assets.gen.dart';
 import '../../resource/string.gen.dart';
-import '../home/home_view.dart';
+
+import '../character/character_view.dart';
 import '../keyboard/keyboard_view.dart';
 import '../mine/mine_view.dart';
 
@@ -23,10 +25,10 @@ class MainController extends BaseController {
       KeyBoardView(),
     ),
     TabBean(
-      StringName.mainTabHome,
-      Assets.images.iconTabHomeUnselect.path,
-      Assets.images.iconTabHomeSelected.path,
-      HomeView(),
+      StringName.mainTabCharacter,
+      Assets.images.iconTabCharacterUnselect.path,
+      Assets.images.iconTabCharacterSelected.path,
+      CharacterView(),
     ),
     TabBean(
       StringName.mainTabMine,
@@ -41,6 +43,11 @@ class MainController extends BaseController {
       _currentIndex.value = index;
     }
   }
+
+  @override
+  void onInit() {
+    super.onInit();
+  }
 }
 
 class TabBean {

+ 34 - 27
lib/module/main/main_page.dart

@@ -43,34 +43,41 @@ class MainPage extends BasePage<MainController> {
   }
 
   Widget bottomAppBarItem(int index) {
-    return Obx(() {
-      TabBean tabBean = controller.tabBeans[index];
-      bool isSelected = controller.currentIndex == index;
-      TextStyle style = TextStyle(
-        fontSize: 10.sp,
-        fontWeight: FontWeight.w400,
-        color:
-            isSelected
-                ? const Color(0xFF7D46FC)
-                : Colors.black.withValues(alpha: 0.4),
-      );
-      String imagePath = isSelected ? tabBean.selectedIcon : tabBean.normalIcon;
-      return GestureDetector(
+    return Expanded(
+      child: GestureDetector(
+        onTap: () => controller.changeIndex(index),
         behavior: HitTestBehavior.opaque,
-        onTap: () {
-          controller.changeIndex(index);
-        },
-        child: SizedBox(
-          height: 56.h,
-          child: Column(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: [
-              Image.asset(imagePath, width: 28.w, height: 28.w),
-              Text(tabBean.title, style: style),
-            ],
-          ),
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Obx(() {
+              TabBean tabBean = controller.tabBeans[index];
+              bool isSelected = controller.currentIndex == index;
+
+              return Column(
+                children: [
+                  Image.asset(
+                    isSelected ? tabBean.selectedIcon : tabBean.normalIcon,
+                    width: 28.w,
+                    height: 28.w,
+                  ),
+                  Text(
+                    tabBean.title,
+                    style: TextStyle(
+                      fontSize: 10.sp,
+                      fontWeight: FontWeight.w400,
+                      color:
+                          isSelected
+                              ? const Color(0xFF7D46FC)
+                              : Colors.black.withValues(alpha: 0.4),
+                    ),
+                  ),
+                ],
+              );
+            }),
+          ],
         ),
-      );
-    });
+      ),
+    );
   }
 }

+ 42 - 34
lib/module/mine/mine_controller.dart

@@ -1,9 +1,16 @@
 import 'package:flutter/rendering.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
-import 'package:keyboard/module/keyboard_setting/keyboard_setting_page.dart';
+import 'package:keyboard/module/about/about_page.dart';
+import 'package:keyboard/module/feedback/feedback_page.dart';
 
+import '../../data/consts/error_code.dart';
 import '../../data/repository/account_repository.dart';
+import '../../resource/string.gen.dart';
+import '../../utils/http_handler.dart';
+import '../../utils/toast_util.dart';
 
 @injectable
 class MineController extends BaseController {
@@ -13,60 +20,61 @@ class MineController extends BaseController {
 
   bool get isLogin => accountRepository.isLogin.value;
 
+  String? get phone => accountRepository.loginPhoneNum.value;
+
   String getUserName() {
-    return '待实现';
+    if (isLogin || phone != null && phone!.length > 4) {
+      return '${StringName.mineAccountLoggedDesc}${phone!.substring(phone!.length - 4)}';
+    } else {
+      return StringName.mineAccountNoLogin;
+    }
   }
 
   clickVip() {
     debugPrint('clickVip');
   }
 
-  clickOrders() {
-    debugPrint('clickOrders');
-  }
-
-  clickPreset() {
-    debugPrint('clickPreset');
-  }
-
-  clickKeyboardSetting() {
-    debugPrint('clickKeyboardSetting');
-    KeyboardSettingPage.start();
-  }
-
   clickOnlineCustomerService() {
     debugPrint('clickOnlineCustomerService');
+    accountRepository
+        .loginUserLogin("11223344551", "1122")
+        .then((data) {
+          Get.back();
+          ToastUtil.show(StringName.loginSuccess);
+        })
+        .catchError((error) {
+          if (error is LoginTooOftenException) {
+            ToastUtil.show(StringName.loginTooOftenToast);
+            return;
+          }
+          if (error is ServerErrorException) {
+            if (error.code == ErrorCode.verificationCodeError) {
+              ToastUtil.show(StringName.loginVerificationCodeErrorToast);
+            } else {
+              ToastUtil.show(error.message);
+            }
+          } else {
+            ToastUtil.show(StringName.loginFailedToast);
+          }
+        });
   }
 
-  clickSetting() {
-    debugPrint('clickSetting');
+  clickTutorials() {
+    debugPrint('clickTutorials');
   }
 
-  clickUserAgreement() {
-    debugPrint('clickUserAgreement');
-  }
-
-  clickPrivacyPolicy() {
-    debugPrint('clickPrivacyPolicy');
+  clickPersonalProfile() {
+    debugPrint('clickPersonalProfile');
   }
 
   clickFeedback() {
     debugPrint('clickFeedback');
-  }
 
-  clickSystemNotification() {
-    debugPrint('clickSystemNotification');
+    FeedbackPage.start();
   }
 
   clickAboutUs() {
     debugPrint('clickAboutUs');
-  }
-
-  clickShare() {
-    debugPrint('clickShare');
-  }
-
-  clickReportComplaint() {
-    debugPrint('clickReportComplaint');
+    AboutPage.start();
   }
 }

+ 85 - 191
lib/module/mine/mine_view.dart

@@ -11,7 +11,7 @@ class MineView extends BaseView<MineController> {
 
   @override
   Color backgroundColor() {
-    return Color(0xFFF6F5FA);
+    return const Color(0xFFF6F5FA);
   }
 
   @override
@@ -20,9 +20,7 @@ class MineView extends BaseView<MineController> {
       children: [
         SafeArea(
           child: SingleChildScrollView(
-            child: Column(
-              children: [heardCard(), baseSettingCard(), functionCard()],
-            ),
+            child: Column(children: [heardCard(), functionCard()]),
           ),
         ),
         IgnorePointer(child: Assets.images.bgMine.image(width: 360.w)),
@@ -55,78 +53,31 @@ class MineView extends BaseView<MineController> {
           baseFunctionButton(
             text: StringName.onlineCustomerService,
             funIcon: Assets.images.iconMineOnlineCustomerService.path,
-            onTap: () => controller.clickOnlineCustomerService(),
-          ),
-          baseFunctionButton(
-            text: StringName.setting,
-            funIcon: Assets.images.iconMineSettings.path,
-            onTap: () => controller.clickSetting(),
+            onTap: controller.clickOnlineCustomerService,
           ),
+
           baseFunctionButton(
-            text: StringName.userAgreement,
-            funIcon: Assets.images.iconMineUserAgreement.path,
-            onTap: () => controller.clickUserAgreement(),
+            text: StringName.tutorials,
+            funIcon: Assets.images.iconMineTutorials.path,
+            onTap: controller.clickTutorials,
           ),
+
           baseFunctionButton(
-            text: StringName.privacyPolicy,
-            funIcon: Assets.images.iconMinePrivacy.path,
-            onTap: () => controller.clickPrivacyPolicy(),
+            text: StringName.personalProfile,
+            funIcon: Assets.images.iconMinePersonalProfile.path,
+            onTap: controller.clickPersonalProfile,
           ),
+
           baseFunctionButton(
             text: StringName.feedback,
             funIcon: Assets.images.iconMineFeedback.path,
-            onTap: () => controller.clickFeedback(),
-          ),
-          baseFunctionButton(
-            text: StringName.systemNotification,
-            funIcon: Assets.images.iconMineSystemNotification.path,
-            onTap: () => controller.clickSystemNotification(),
+            onTap: controller.clickFeedback,
           ),
+
           baseFunctionButton(
             text: StringName.aboutUs,
             funIcon: Assets.images.iconMineAbout.path,
-            onTap: () => controller.clickAboutUs(),
-          ),
-          baseFunctionButton(
-            text: StringName.shareApp,
-            funIcon: Assets.images.iconMineShare.path,
-            onTap: () => controller.clickShare(),
-          ),
-          baseFunctionButton(
-            text: StringName.reportComplaint,
-            funIcon: Assets.images.iconMineReportComplaint.path,
-            onTap: () => controller.clickReportComplaint(),
-          ),
-        ],
-      ),
-    );
-  }
-
-  Widget baseSettingCard() {
-    return Container(
-      margin: EdgeInsets.only(top: 13.w, left: 16.w, right: 16.w),
-      child: Row(
-        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-        children: [
-          baseSettingButton(
-            text: StringName.preset,
-            btnIcon: Assets.images.iconMinePreset.image(
-              width: 20.w,
-              height: 20.h,
-            ),
-            onTap: () {
-              controller.clickPreset();
-            },
-          ),
-          baseSettingButton(
-            text: StringName.keyboardSetting,
-            btnIcon: Assets.images.iconMineKeyboardSetting.image(
-              width: 20.w,
-              height: 20.h,
-            ),
-            onTap: () {
-              controller.clickKeyboardSetting();
-            },
+            onTap: controller.clickAboutUs,
           ),
         ],
       ),
@@ -144,23 +95,15 @@ class MineView extends BaseView<MineController> {
               height: 56.r,
             ),
         SizedBox(width: 12.r),
-        controller.isLogin
-            ? Text(
-              controller.getUserName(),
-              style: TextStyle(
-                fontSize: 18.sp,
-                color: Colors.black,
-                fontWeight: FontWeight.w500,
-              ),
-            )
-            : Text(
-              StringName.loginAccount,
-              style: TextStyle(
-                fontSize: 18.sp,
-                color: Colors.black,
-                fontWeight: FontWeight.w500,
-              ),
-            ),
+        Text(
+          controller.getUserName(),
+          style: TextStyle(
+            fontSize: 18.sp,
+            color: Colors.black,
+            fontWeight: FontWeight.w500,
+          ),
+        ),
+
         SizedBox(width: 4.r),
 
         Assets.images.iconMineLoginArrow.image(width: 16.r, height: 16.r),
@@ -173,7 +116,7 @@ class MineView extends BaseView<MineController> {
     return Container(
       margin: EdgeInsets.only(top: 21.w),
       width: 326.w,
-      height: 108.w,
+      height: 79.w,
       decoration: ShapeDecoration(
         image: DecorationImage(
           image: Assets.images.bgMineVipCard.provider(),
@@ -183,132 +126,83 @@ class MineView extends BaseView<MineController> {
           borderRadius: BorderRadius.circular(11.r),
         ),
       ),
-      child: Stack(
-        children: [
-          Positioned(
-            top: 19.w,
-            left: 15.w,
-            right: 15.w,
-            child: GestureDetector(
-              behavior: HitTestBehavior.opaque,
-              onTap: controller.clickVip,
-              child: SizedBox(
-                width: 326.w,
-                child: Row(
-                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                  children: [
-                    Column(
-                      crossAxisAlignment: CrossAxisAlignment.start,
-                      children: [
-                        // vip 图标
-                        Assets.images.iconMineVip.image(
-                          width: 62.w,
-                          height: 19.h,
-                        ),
-                        // vip描述文本
-                        Row(
-                          children: [
-                            Text(
-                              StringName.vipLevel0Desc,
-                              style: TextStyle(
-                                color: Color(0xFFE4B483),
-                                fontSize: 12.sp,
-                                fontWeight: FontWeight.w400,
-                              ),
-                            ),
-                            Assets.images.iconMineVipDescArrow.image(
-                              width: 16.w,
-                              height: 16.h,
-                            ),
-                          ],
-                        ),
-                      ],
-                    ),
-                    // VIP按钮
-                    Container(
-                      alignment: Alignment.center,
-                      width: 80.w,
-                      height: 28.h,
-                      decoration: ShapeDecoration(
-                        gradient: LinearGradient(
-                          begin: Alignment(-0.94.r, 0.35.r),
-                          end: Alignment(0.94.r, -0.35.r),
-                          colors: [
-                            Color(0xFFFFD79C),
-                            Color(0xFFF19F45),
-                            Color(0xFFF19F45),
-                            Color(0xFFFFC387),
-                          ],
-                        ),
-                        shape: RoundedRectangleBorder(
-                          side: BorderSide(
-                            width: 1.w,
-                            color: Color(0xFFEA973E),
-                          ),
-                          borderRadius: BorderRadius.circular(32.r),
+      child: GestureDetector(
+        behavior: HitTestBehavior.opaque,
+        onTap: controller.clickVip,
+        child: Container(
+          padding: EdgeInsets.only(left: 15.w, right: 15.w),
+          width: 326.w,
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  // vip 图标
+                  Assets.images.iconMineVip.image(width: 62.w, height: 19.h),
+                  // vip描述文本
+                  Row(
+                    children: [
+                      Text(
+                        StringName.vipLevel0Desc,
+                        style: TextStyle(
+                          color: Color(0xFFE4B483),
+                          fontSize: 12.sp,
+                          fontWeight: FontWeight.w400,
                         ),
                       ),
-                      child: Row(
-                        mainAxisAlignment: MainAxisAlignment.center,
-                        children: [
-                          Text(
-                            StringName.vipLevel0Btn,
-                            textAlign: TextAlign.center,
-                            style: TextStyle(
-                              color: Colors.white,
-                              fontSize: 13.sp,
-                              fontWeight: FontWeight.w400,
-                            ),
-                          ),
-                          Assets.images.iconMineVipArrow.image(
-                            width: 10.w,
-                            height: 10.w,
-                          ),
-                        ],
+                      Assets.images.iconMineVipDescArrow.image(
+                        width: 16.w,
+                        height: 16.h,
                       ),
-                    ),
-                  ],
-                ),
+                    ],
+                  ),
+                ],
               ),
-            ),
-          ),
-
-          //   我的订单
-          Positioned(
-            bottom: 7.h,
-            left: 16.w,
-            child: GestureDetector(
-              onTap: controller.clickOrders,
-              behavior: HitTestBehavior.opaque,
-              child: SizedBox(
-                width: 320.w,
+              // VIP按钮
+              Container(
+                alignment: Alignment.center,
+                width: 80.w,
+                height: 28.h,
+                decoration: ShapeDecoration(
+                  gradient: LinearGradient(
+                    begin: Alignment(-0.94.r, 0.35.r),
+                    end: Alignment(0.94.r, -0.35.r),
+                    colors: [
+                      Color(0xFFFFD79C),
+                      Color(0xFFF19F45),
+                      Color(0xFFF19F45),
+                      Color(0xFFFFC387),
+                    ],
+                  ),
+                  shape: RoundedRectangleBorder(
+                    side: BorderSide(width: 1.w, color: Color(0xFFEA973E)),
+                    borderRadius: BorderRadius.circular(32.r),
+                  ),
+                ),
                 child: Row(
                   mainAxisAlignment: MainAxisAlignment.center,
                   children: [
-                    Assets.images.iconMineOrderLogo.image(
-                      width: 16.w,
-                      height: 16.h,
-                    ),
-                    SizedBox(width: 2.w),
                     Text(
-                      StringName.myOrders,
+                      StringName.vipLevel0Btn,
+                      textAlign: TextAlign.center,
                       style: TextStyle(
-                        color: Color(0xFFE79F63),
-                        fontSize: 12.sp,
+                        color: Colors.white,
+                        fontSize: 13.sp,
                         fontWeight: FontWeight.w400,
                       ),
                     ),
-                    Assets.images.iconMineVipOrderArrow.image(
-                      width: 16.w,
-                      height: 16.h,
+                    Assets.images.iconMineVipArrow.image(
+                      width: 10.w,
+                      height: 10.w,
                     ),
-                    Expanded(child: SizedBox.shrink()),
                   ],
                 ),
               ),
-            ),
+            ],
           ),
-        ],
+        ),
       ),
     );
   }

+ 135 - 0
lib/plugins/keyboard_android_platform.dart

@@ -0,0 +1,135 @@
+import 'dart:convert';
+
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/data/repository/chat_repository.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+import 'package:keyboard_android/keyboard_android.dart';
+
+import '../data/bean/stream_deepseek_data.dart' as deepseek_data;
+import '../di/get_it.dart';
+
+@lazySingleton
+class KeyboardAndroidPlatform {
+  static const MethodChannel _channel = MethodChannel('keyboard_android');
+  var tag = "KeyboardAndroidPlatform";
+  final ChatRepository chatRepository;
+
+  KeyboardAndroidPlatform(this.chatRepository) {
+    AtmobLog.d(tag, '$tag....init');
+    init();
+  }
+
+  void init() {
+    _channel.setMethodCallHandler((MethodCall call) async {
+      switch (call.method) {
+        case 'getKeyMappings':
+          // 返回键映射
+          return [
+            {'label': 'A', 'method': 'MethodA'},
+            {'label': 'B', 'method': 'MethodB'},
+            {'label': 'C', 'method': 'MethodC'},
+            {'label': 'D', 'method': 'MethodD'},
+            {'label': 'E', 'method': 'MethodE'},
+          ];
+        case 'sendDynamicTextRequest':
+          String method = call.arguments['method'];
+          String currentContent = call.arguments['currentContent'];
+          AtmobLog.d(tag, 'method: $method, currentContent: $currentContent');
+          sendDynamicTextRequest(method, currentContent);
+          break;
+        default:
+          throw MissingPluginException('Not implemented');
+      }
+    });
+  }
+
+  Future<void> sendDynamicTextRequest(
+    String method,
+    String currentContent,
+  ) async {
+    switch (method) {
+      case 'MethodA':
+        AtmobLog.d(tag, 'MethodA');
+        deepSeek(message: currentContent);
+        break;
+      default:
+        AtmobLog.d(tag, 'default');
+    }
+  }
+
+  void enableFloatingWindow(bool enable) {
+    debugPrint('enableFloatingWindow $enable');
+    KeyboardAndroid.enableFloatingWindow(enable);
+  }
+
+  void openInputMethodSettings() {
+    debugPrint('openInputMethodSettings');
+    KeyboardAndroid.openInputMethodSettings();
+  }
+
+  Future<bool> isTargetKeyboardEnabled() {
+    var enable = KeyboardAndroid.isTargetKeyboardEnabled();
+    debugPrint('isTargetKeyboardEnabled value : $enable');
+    return enable;
+  }
+
+  void inputText(String text) {
+    debugPrint('inputText $text');
+
+    _channel.invokeMethod('inputText', {'text': text});
+  }
+
+  Future<void> deepSeek({required String message}) async {
+    try {
+      // 获取数据流
+      var stream = await chatRepository.streamDeepSeek(message);
+
+      // 创建一个 StringBuffer 来拼接流中的字符串数据
+      StringBuffer responseBuffer = StringBuffer();
+
+      // 监听流
+      await for (var event in stream) {
+        try {
+          // 解析JSON数据
+          Map<String, dynamic> json = jsonDecode(event.data);
+
+          // 如果数据不为空,则添加到 responseBuffer
+          if (json.isNotEmpty) {
+            deepseek_data.StreamDeepseekData data = deepseek_data
+                .StreamDeepseekData.fromJson(json);
+            if (data.choices == null || data.choices!.isEmpty) {
+              AtmobLog.d(tag, "data.choices == null || data.choices!.isEmpty");
+            }
+            deepseek_data.Delta? delta = data.choices![0].delta;
+
+            if (delta == null) {
+              AtmobLog.d(tag, "delta == null");
+              return;
+            }
+            // 有内容时才输入
+            if (delta.content != null) {
+              inputText(delta.content!);
+            }
+
+            responseBuffer.write(delta.content ?? '');
+          }
+        } catch (e) {
+          // 捕获 JSON 解析错误
+          print('Error parsing JSON: $e');
+        }
+      }
+
+      AtmobLog.d(tag, "responseBuffer.toString() ${responseBuffer.toString()}");
+    } catch (error) {
+      // 捕获请求错误
+
+      AtmobLog.d(tag, "Error in deepSeek: $error");
+    }
+  }
+
+  static KeyboardAndroidPlatform getInstance() {
+    return getIt.get<KeyboardAndroidPlatform>();
+  }
+}

+ 36 - 51
lib/resource/assets.gen.dart

@@ -12,6 +12,14 @@ import 'package:flutter/widgets.dart';
 class $AssetsImagesGen {
   const $AssetsImagesGen();
 
+  /// File path: assets/images/bg_character_boy_banner.webp
+  AssetGenImage get bgCharacterBoyBanner =>
+      const AssetGenImage('assets/images/bg_character_boy_banner.webp');
+
+  /// File path: assets/images/bg_character_girl_banner.webp
+  AssetGenImage get bgCharacterGirlBanner =>
+      const AssetGenImage('assets/images/bg_character_girl_banner.webp');
+
   /// File path: assets/images/bg_mine.webp
   AssetGenImage get bgMine => const AssetGenImage('assets/images/bg_mine.webp');
 
@@ -19,6 +27,14 @@ class $AssetsImagesGen {
   AssetGenImage get bgMineVipCard =>
       const AssetGenImage('assets/images/bg_mine_vip_card.webp');
 
+  /// File path: assets/images/icon_about_arrow_left.webp
+  AssetGenImage get iconAboutArrowLeft =>
+      const AssetGenImage('assets/images/icon_about_arrow_left.webp');
+
+  /// File path: assets/images/icon_black_back.webp
+  AssetGenImage get iconBlackBack =>
+      const AssetGenImage('assets/images/icon_black_back.webp');
+
   /// File path: assets/images/icon_mine_about.webp
   AssetGenImage get iconMineAbout =>
       const AssetGenImage('assets/images/icon_mine_about.webp');
@@ -35,10 +51,6 @@ class $AssetsImagesGen {
   AssetGenImage get iconMineFeedback =>
       const AssetGenImage('assets/images/icon_mine_feedback.webp');
 
-  /// File path: assets/images/icon_mine_keyboard_setting.webp
-  AssetGenImage get iconMineKeyboardSetting =>
-      const AssetGenImage('assets/images/icon_mine_keyboard_setting.webp');
-
   /// File path: assets/images/icon_mine_login_arrow.webp
   AssetGenImage get iconMineLoginArrow =>
       const AssetGenImage('assets/images/icon_mine_login_arrow.webp');
@@ -48,37 +60,13 @@ class $AssetsImagesGen {
     'assets/images/icon_mine_online_customer_service.webp',
   );
 
-  /// File path: assets/images/icon_mine_order_logo.webp
-  AssetGenImage get iconMineOrderLogo =>
-      const AssetGenImage('assets/images/icon_mine_order_logo.webp');
-
-  /// File path: assets/images/icon_mine_preset.webp
-  AssetGenImage get iconMinePreset =>
-      const AssetGenImage('assets/images/icon_mine_preset.webp');
-
-  /// File path: assets/images/icon_mine_privacy.webp
-  AssetGenImage get iconMinePrivacy =>
-      const AssetGenImage('assets/images/icon_mine_privacy.webp');
-
-  /// File path: assets/images/icon_mine_report_complaint.webp
-  AssetGenImage get iconMineReportComplaint =>
-      const AssetGenImage('assets/images/icon_mine_report_complaint.webp');
-
-  /// File path: assets/images/icon_mine_settings.webp
-  AssetGenImage get iconMineSettings =>
-      const AssetGenImage('assets/images/icon_mine_settings.webp');
-
-  /// File path: assets/images/icon_mine_share.webp
-  AssetGenImage get iconMineShare =>
-      const AssetGenImage('assets/images/icon_mine_share.webp');
-
-  /// File path: assets/images/icon_mine_system_notification.webp
-  AssetGenImage get iconMineSystemNotification =>
-      const AssetGenImage('assets/images/icon_mine_system_notification.webp');
+  /// File path: assets/images/icon_mine_personal_profile.webp
+  AssetGenImage get iconMinePersonalProfile =>
+      const AssetGenImage('assets/images/icon_mine_personal_profile.webp');
 
-  /// File path: assets/images/icon_mine_user_agreement.webp
-  AssetGenImage get iconMineUserAgreement =>
-      const AssetGenImage('assets/images/icon_mine_user_agreement.webp');
+  /// File path: assets/images/icon_mine_tutorials.webp
+  AssetGenImage get iconMineTutorials =>
+      const AssetGenImage('assets/images/icon_mine_tutorials.webp');
 
   /// File path: assets/images/icon_mine_user_logged.webp
   AssetGenImage get iconMineUserLogged =>
@@ -104,13 +92,13 @@ class $AssetsImagesGen {
   AssetGenImage get iconMineVipOrderArrow =>
       const AssetGenImage('assets/images/icon_mine_vip_order_arrow.png');
 
-  /// File path: assets/images/icon_tab_home_selected.webp
-  AssetGenImage get iconTabHomeSelected =>
-      const AssetGenImage('assets/images/icon_tab_home_selected.webp');
+  /// File path: assets/images/icon_tab_character_selected.webp
+  AssetGenImage get iconTabCharacterSelected =>
+      const AssetGenImage('assets/images/icon_tab_character_selected.webp');
 
-  /// File path: assets/images/icon_tab_home_unselect.webp
-  AssetGenImage get iconTabHomeUnselect =>
-      const AssetGenImage('assets/images/icon_tab_home_unselect.webp');
+  /// File path: assets/images/icon_tab_character_unselect.webp
+  AssetGenImage get iconTabCharacterUnselect =>
+      const AssetGenImage('assets/images/icon_tab_character_unselect.webp');
 
   /// File path: assets/images/icon_tab_keyboard_selected.webp
   AssetGenImage get iconTabKeyboardSelected =>
@@ -130,31 +118,28 @@ class $AssetsImagesGen {
 
   /// List of all assets
   List<AssetGenImage> get values => [
+    bgCharacterBoyBanner,
+    bgCharacterGirlBanner,
     bgMine,
     bgMineVipCard,
+    iconAboutArrowLeft,
+    iconBlackBack,
     iconMineAbout,
     iconMineArrow,
     iconMineBackArrow,
     iconMineFeedback,
-    iconMineKeyboardSetting,
     iconMineLoginArrow,
     iconMineOnlineCustomerService,
-    iconMineOrderLogo,
-    iconMinePreset,
-    iconMinePrivacy,
-    iconMineReportComplaint,
-    iconMineSettings,
-    iconMineShare,
-    iconMineSystemNotification,
-    iconMineUserAgreement,
+    iconMinePersonalProfile,
+    iconMineTutorials,
     iconMineUserLogged,
     iconMineUserNoLogin,
     iconMineVip,
     iconMineVipArrow,
     iconMineVipDescArrow,
     iconMineVipOrderArrow,
-    iconTabHomeSelected,
-    iconTabHomeUnselect,
+    iconTabCharacterSelected,
+    iconTabCharacterUnselect,
     iconTabKeyboardSelected,
     iconTabKeyboardUnselect,
     iconTabMineSelected,

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

@@ -1,82 +0,0 @@
-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 mainTabHome = 'main_tab_home'.tr; // 人设
-  static final String preset = 'preset'.tr; // 基础预设
-  static final String keyboardSetting = 'keyboard_setting'.tr; // 键盘设置
-  static final String onlineCustomerService = 'online_customer_service'.tr; // 在线客服
-  static final String setting = 'setting'.tr; // 设置
-  static final String userAgreement = 'user_agreement'.tr; // 用户协议
-  static final String privacyPolicy = 'privacy_policy'.tr; // 隐私政策
-  static final String feedback = 'feedback'.tr; // 意见反馈
-  static final String systemNotification = 'system_notification'.tr; // 系统消息
-  static final String aboutUs = 'about_us'.tr; // 关于我们
-  static final String shareApp = 'share_app'.tr; // 分享应用
-  static final String reportComplaint = 'report_complaint'.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 myOrders = 'my_orders'.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; // 登录失败
-}
-class StringMultiSource {
-  StringMultiSource._();
-  static const Map<String, Map<String, String>> values = {
-    'zh_CN': {
-      'app_name': '追爱小键盘',
-      'main_tab_keyboard': '键盘',
-      'main_tab_mine': '我的',
-      'main_tab_home': '人设',
-      'preset': '基础预设',
-      'keyboard_setting': '键盘设置',
-      'online_customer_service': '在线客服',
-      'setting': '设置',
-      'user_agreement': '用户协议',
-      'privacy_policy': '隐私政策',
-      'feedback': '意见反馈',
-      'system_notification': '系统消息',
-      'about_us': '关于我们',
-      'share_app': '分享应用',
-      'report_complaint': '投诉举报',
-      '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': '立即查看',
-      'my_orders': '我的订单',
-      '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': '登录失败',
-    },
-  };
-}

+ 23 - 7
lib/router/app_pages.dart

@@ -1,12 +1,18 @@
 import 'package:get/get.dart';
-import 'package:keyboard/module/home/home_controller.dart';
+import 'package:keyboard/module/about/about_controller.dart';
+import 'package:keyboard/module/browser/browser_controller.dart';
+import 'package:keyboard/module/feedback/feedback_controller.dart';
 import 'package:keyboard/module/keyboard/keyboard_controller.dart';
-import 'package:keyboard/module/keyboard/keyboard_view.dart';
-import 'package:keyboard/module/keyboard_setting/keyboard_setting_controller.dart';
-import 'package:keyboard/module/keyboard_setting/keyboard_setting_page.dart';
+
+import 'package:keyboard/module/login/login_controller.dart';
 import 'package:keyboard/module/mine/mine_controller.dart';
 
 import '../di/get_it.dart';
+import '../module/about/about_page.dart';
+import '../module/browser/browser_page.dart';
+import '../module/character/character_controller.dart';
+import '../module/feedback/feedback_page.dart';
+import '../module/login/login_page.dart';
 import '../module/main/main_controller.dart';
 import '../module/main/main_page.dart';
 
@@ -17,7 +23,11 @@ abstract class AppPage {
 abstract class RoutePath {
   static const splash = '/';
   static const mainTab = '/mainTab';
+  static const login = '/login';
   static const keyboardSetting = '/keyboardSetting';
+  static const feedback = '/feedback';
+  static const about = '/about';
+  static const browser = '/browser';
 }
 
 class AppBinding extends Bindings {
@@ -25,9 +35,12 @@ class AppBinding extends Bindings {
   void dependencies() {
     lazyPut(() => getIt.get<MainController>());
     lazyPut(() => getIt.get<MineController>());
-    lazyPut(() => getIt.get<HomeController>());
+    lazyPut(() => getIt.get<CharacterController>());
+    lazyPut(() => getIt.get<LoginController>());
     lazyPut(() => getIt.get<KeyBoardController>());
-    lazyPut(() => getIt.get<KeyboardSettingController>());
+    lazyPut(() => getIt.get<FeedbackController>());
+    lazyPut(() => getIt.get<AboutController>());
+    lazyPut(() => getIt.get<BrowserController>());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -37,5 +50,8 @@ class AppBinding extends Bindings {
 
 final generalPages = [
   GetPage(name: RoutePath.mainTab, page: () => MainPage()),
-  GetPage(name: RoutePath.keyboardSetting, page: () => KeyboardSettingPage()),
+  GetPage(name: RoutePath.login, page: () => LoginPage()),
+  GetPage(name: RoutePath.feedback, page: () => FeedbackPage()),
+  GetPage(name: RoutePath.about, page: () => AboutPage()),
+  GetPage(name: RoutePath.browser, page: () => BrowserPage()),
 ];

+ 25 - 0
lib/utils/error_handler.dart

@@ -0,0 +1,25 @@
+import 'package:keyboard/utils/toast_util.dart';
+
+import '../data/consts/error_code.dart';
+import '../resource/string.gen.dart';
+import 'http_handler.dart';
+
+class ErrorHandler {
+  ErrorHandler._();
+
+  static void toastError(dynamic error, {String? message}) {
+    String toastMessage =
+        (error is ServerErrorException)
+            ? _getToastMessageFromError(error)
+            : _getDefaultToastMessage(message);
+    ToastUtil.show(toastMessage);
+  }
+
+  static String _getToastMessageFromError(ServerErrorException error) {
+    return error.code?.description ?? error.message ?? StringName.networkError;
+  }
+
+  static String _getDefaultToastMessage(String? message) {
+    return message ?? StringName.networkError;
+  }
+}

+ 1 - 1
lib/utils/privacy_compliance.dart

@@ -5,7 +5,7 @@ class PrivacyCompliance {
 
   static const String isPolicyGranted = 'isPolicyGranted';
   static final List<EnsurePolicyGrant> _pendingTasks = [];
-  static bool _isPolicyGranted = false;
+  static bool _isPolicyGranted = true;
 
   static Future<void> ensurePolicyGranted(EnsurePolicyGrant ensure) async {
     if (isAgreePrivacyPolicy()) {

+ 142 - 0
lib/utils/sse_parse_util.dart

@@ -0,0 +1,142 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+
+class SSEParseUtil {
+  static Stream<Message> parse(Stream<Uint8List> stream) {
+    return stream.transform(SSETransformer());
+  }
+}
+
+class Message {
+  final String id;
+  final String event;
+  final String data;
+  final int? retry;
+
+  Message(
+      {required this.id,
+        required this.event,
+        required this.data,
+        required this.retry});
+
+  @override
+  String toString() {
+    return 'Message{id: $id, event: $event, data: $data, retry: $retry}';
+  }
+}
+
+class SSETransformer extends StreamTransformerBase<Uint8List, Message> {
+  @override
+  Stream<Message> bind(Stream<Uint8List> stream) {
+    return Stream.eventTransformed(
+      stream.map((uint8List) => List<int>.from(uint8List)),
+          (sink) => SSESink(sink),
+    );
+  }
+}
+
+class SSESink implements EventSink<List<int>> {
+  static final _eventSeparator = utf8.encode("\n\n");
+  static const _fieldSeparator = "\n";
+  static const _dataPrefix = "data:";
+  static const _dataPrefixR = "data: ";
+  static const _idPrefix = "id:";
+  static const _idPrefixR = "id: ";
+  static const _eventPrefix = "event:";
+  static const _eventPrefixR = "event: ";
+  static const _retryPrefix = "retry:";
+  static const _retryPrefixR = "retry: ";
+  static const _commentPrefix = ":";
+  static const _commentPrefixR = ": ";
+
+  final EventSink<Message> _eventSink;
+
+  final List<int> _buffer = [];
+
+  SSESink(this._eventSink);
+
+  @override
+  void add(List<int> event) {
+    _buffer.addAll(event);
+
+    final endIndex = _indexOf(_buffer, _eventSeparator);
+    if (endIndex == -1) {
+      return;
+    }
+
+    final completedEvent = _buffer.sublist(0, endIndex);
+    _buffer.removeRange(0, endIndex + _eventSeparator.length);
+
+    parseEvent(completedEvent);
+  }
+
+  @override
+  void addError(Object error, [StackTrace? stackTrace]) {
+    _eventSink.addError(error, stackTrace);
+  }
+
+  @override
+  void close() {
+    _eventSink.close();
+  }
+
+  int _indexOf(List<int> origin, List<int> target) {
+    for (var i = 0; i < origin.length - target.length; i++) {
+      var found = true;
+      for (var j = 0; j < target.length; j++) {
+        if (origin[i + j] != target[j]) {
+          found = false;
+          break;
+        }
+      }
+      if (found) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  void parseEvent(List<int> completedEvent) {
+    final eventString = utf8.decode(completedEvent);
+    final fields = eventString.split(_fieldSeparator);
+
+    String? id;
+    String? event;
+    String data = "";
+    int? retry;
+
+    for (final field in fields) {
+      final trimmedField = field.trim();
+      if (trimmedField.isEmpty) {
+        continue;
+      }
+
+      if (trimmedField.startsWith(_commentPrefix) ||
+          trimmedField.startsWith(_commentPrefixR)) {
+        continue;
+      }
+
+      if (trimmedField.startsWith(_retryPrefixR)) {
+        retry = int.tryParse(trimmedField.substring(_retryPrefixR.length));
+      } else if (trimmedField.startsWith(_dataPrefixR)) {
+        data += trimmedField.substring(_dataPrefixR.length);
+      } else if (trimmedField.startsWith(_eventPrefixR)) {
+        event = trimmedField.substring(_eventPrefixR.length);
+      } else if (trimmedField.startsWith(_idPrefixR)) {
+        id = trimmedField.substring(_idPrefixR.length);
+      } else if (trimmedField.startsWith(_idPrefix)) {
+        id = trimmedField.substring(_idPrefix.length);
+      } else if (trimmedField.startsWith(_eventPrefix)) {
+        event = trimmedField.substring(_eventPrefix.length);
+      } else if (trimmedField.startsWith(_dataPrefix)) {
+        data += trimmedField.substring(_dataPrefix.length);
+      } else if (trimmedField.startsWith(_retryPrefix)) {
+        retry = int.tryParse(trimmedField.substring(_retryPrefix.length));
+      }
+    }
+
+    _eventSink.add(Message(
+        id: id ?? "", event: event ?? "", data: data, retry: retry));
+  }
+}

+ 52 - 0
lib/widget/commonAppBar.dart

@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+
+import '../resource/assets.gen.dart';
+
+class CommonAppBar extends StatelessWidget implements PreferredSizeWidget {
+  final String title;
+  final VoidCallback? onBack;
+  final Color Function() backgroundColor;
+
+  const CommonAppBar({
+    super.key,
+    required this.title,
+    required this.backgroundColor,
+    this.onBack,
+  });
+
+  @override
+  @override
+  Widget build(BuildContext context) {
+    return AppBar(
+      scrolledUnderElevation: 0,
+      backgroundColor: backgroundColor(),
+      leadingWidth: 40.w,
+      leading: Padding(
+        padding: EdgeInsets.only(left: 16.w),
+        child: GestureDetector(
+          onTap: onBack ?? Get.back,
+          child: Assets.images.iconMineBackArrow.image(
+            width: 24.w,
+            height: 24.h,
+          ),
+        ),
+      ),
+      centerTitle: true,
+      title: Text(
+        title,
+        textAlign: TextAlign.center,
+        style: TextStyle(
+          color: Colors.black.withAlpha(204),
+          fontSize: 17.sp,
+          fontWeight: FontWeight.w500,
+          height: 1.18.h,
+        ),
+      ),
+    );
+  }
+
+  @override
+  Size get preferredSize => Size.fromHeight(kToolbarHeight);
+}

+ 7 - 9
pubspec.lock

@@ -661,6 +661,13 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.4.14"
+  keyboard_android:
+    dependency: "direct main"
+    description:
+      path: "D:\\flutterProject\\keyboard_android"
+      relative: false
+    source: path
+    version: "0.0.1"
   leak_tracker:
     dependency: transitive
     description:
@@ -789,15 +796,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.0.0"
-  oaid:
-    dependency: "direct main"
-    description:
-      path: "."
-      ref: "v0.0.1"
-      resolved-ref: "22c60c77cdbc3aa91277d83a711c659b74d24227"
-      url: "http://git.atmob.com:28999/Atmob-Flutter/Oaid.git"
-    source: git
-    version: "0.0.1"
   octo_image:
     dependency: transitive
     description:

+ 8 - 5
pubspec.yaml

@@ -63,11 +63,14 @@ dependencies:
       name: atmob_channel_reader
       url: http://pub.v8dashen.com/
 
-  #oaid
-  oaid:
-    git:
-      url: http://git.atmob.com:28999/Atmob-Flutter/Oaid.git
-      ref: v0.0.1
+#  #oaid
+#  oaid:
+#    git:
+#      url: http://git.atmob.com:28999/Atmob-Flutter/Oaid.git
+#      ref: v0.0.1
+
+  keyboard_android:
+    path: D:/flutterProject/keyboard_android
 
 
   cupertino_icons: ^1.0.8