Преглед изворни кода

Merge remote-tracking branch 'origin/v1.0.0' into v1.0.0

云天逵 пре 8 месеци
родитељ
комит
f1e66cac57

BIN
assets/images/keyboard_tutorial_ios_step_1.webp


BIN
assets/images/keyboard_tutorial_ios_step_2.webp


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

@@ -234,6 +234,10 @@
     <string name="tutorial_enable_keyboard_step">1.启用键盘</string>
     <string name="tutorial_enable_floating_ball_step">2.开启悬浮球</string>
 
+    <string name="tutorial_enable_keyboard_ios">开启追爱小键盘</string>
+    <string name="tutorial_enable_keyboard_ios_step1">1.点击[键盘]</string>
+    <string name="tutorial_enable_keyboard_ios_step2">2.开启【追爱小键盘】,并【允许完全访问】</string>
+
     <string name="profile_save">完成</string>
     <string name="profile_edit_save">保存</string>
     <string name="profile_list">档案列表</string>

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

@@ -70,6 +70,10 @@ import '../module/intro/intro_controller.dart' as _i211;
 import '../module/keyboard/keyboard_controller.dart' as _i161;
 import '../module/keyboard_guide/keyboard_guide_controller.dart' as _i248;
 import '../module/keyboard_manage/keyboard_manage_controller.dart' as _i922;
+import '../module/keyboard_tutorial/android/keyboard_tutorial_android_view_controller.dart'
+    as _i1022;
+import '../module/keyboard_tutorial/ios/keyboard_tutorial_ios_view_controller.dart'
+    as _i842;
 import '../module/keyboard_tutorial/keyboard_tutorial_controller.dart' as _i507;
 import '../module/login/login_controller.dart' as _i1008;
 import '../module/main/main_controller.dart' as _i731;
@@ -139,6 +143,12 @@ extension GetItInjectableX on _i174.GetIt {
     gh.factory<_i415.KeyboardMethodHandler>(
       () => _i415.KeyboardMethodHandler(),
     );
+    gh.factory<_i1022.KeyboardTutorialAndroidViewController>(
+      () => _i1022.KeyboardTutorialAndroidViewController(),
+    );
+    gh.factory<_i842.KeyboardTutorialIosViewController>(
+      () => _i842.KeyboardTutorialIosViewController(),
+    );
     gh.lazySingleton<_i495.WechatLoginService>(
       () => _i495.WechatLoginService(),
     );

+ 188 - 0
lib/module/keyboard_tutorial/android/keyboard_tutorial_android_view.dart

@@ -0,0 +1,188 @@
+import 'package:flutter/Material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:lottie/lottie.dart';
+
+import '../../../resource/assets.gen.dart';
+import '../../../resource/colors.gen.dart';
+import '../../../resource/string.gen.dart';
+import '../../../utils/status_bar_util.dart';
+import '../../../widget/app_lifecycle_widget.dart';
+import '../../../widget/gradient_btn.dart';
+import '../../../widget/status_bar_placeholder_widget.dart';
+import '../../../widget/top_bar.dart';
+import '../widget/tutorial_header_widget.dart';
+import 'keyboard_tutorial_android_view_controller.dart';
+
+/// 键盘教程页-Android平台
+class KeyboardTutorialAndroidView
+    extends BaseView<KeyboardTutorialAndroidViewController> {
+  const KeyboardTutorialAndroidView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return AppLifecycleWidget(
+      onAppLifecycleCallback: (isForeground) async {
+        // 步骤都做完了,则跳转去键盘引导页
+        if (await controller.checkHasComplete()) {
+          return;
+        }
+        // 未完成,则每次切换到前台时,重新检查设置,更新按钮状态
+        if (isForeground) {
+          controller.checkSetting();
+        }
+      },
+      child: Stack(
+        children: [
+          // 头部
+          Positioned(left: 0, top: 0, right: 0, child: _buildHeader(context)),
+          // 状态栏和标题栏
+          Positioned(
+            left: 0,
+            top: 0,
+            right: 0,
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: [StatusBarPlaceholderWidget(), _buildTopBar()],
+            ),
+          ),
+          // 内容,填充剩余部分
+          Positioned.fill(
+            top: 170.h + StatusBarUtil.getStatusBarHeight(Get.context!),
+            left: 0,
+            right: 0,
+            child: Column(children: [_buildContent()]),
+          ),
+          // 底部按钮
+          Positioned(
+            left: 0,
+            right: 0,
+            bottom: 0,
+            child: _buildBottomActionBtn(),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 顶部栏
+  Widget _buildTopBar() {
+    return TopBar(
+      // 返回按钮
+      leftWidget: GestureDetector(
+        onTap: controller.clickBack,
+        child: Assets.images.iconBlackBackArrow.image(
+          width: 24.w,
+          height: 24.h,
+        ),
+      ),
+      // 视频教程按钮
+      rightWidget: Positioned(
+        right: 16.w,
+        child: Visibility(
+          visible: true,
+          child: GestureDetector(
+            onTap: () {
+              controller.clickTutorialVideo();
+            },
+            child: Text(
+              StringName.keyboardTutorialVideo,
+              style: TextStyle(
+                color: ColorName.black85,
+                fontSize: 14.sp,
+                fontWeight: FontWeight.w500,
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 头部布局
+  Widget _buildHeader(BuildContext context) {
+    return TutorialHeaderWidget();
+  }
+
+  /// 内容
+  Widget _buildContent() {
+    return Expanded(child: Column(children: [_buildTutorialAnimation()]));
+  }
+
+  /// 键盘设置引导动画
+  Widget _buildTutorialAnimation() {
+    return Lottie.asset(
+      Assets.anim.animKeyboardTutorialSetting,
+      repeat: true,
+      width: 340.w,
+    );
+  }
+
+  /// 启用键盘按钮
+  Widget _buildEnableKeyboard() {
+    return Obx(() {
+      return Container(
+        margin: EdgeInsets.symmetric(horizontal: 50.w),
+        child: GradientTextBtn(
+          // 是否启用,这里UI设计上,未启用时要高亮按钮,所以要取反
+          enable: !controller.isKeyboardEnable.value,
+          StringName.tutorialEnableKeyboardStep,
+          onPressed: () {
+            // 已启用,不响应点击
+            if (controller.isKeyboardEnable.value) {
+              return;
+            }
+            // 去启用键盘
+            controller.goEnableKeyboard();
+          },
+        ),
+      );
+    });
+  }
+
+  /// 悬浮球设置按钮
+  Widget _buildFloatingBallActionBtn() {
+    return Obx(() {
+      bool isKeyboardEnable = controller.isKeyboardEnable.value;
+      bool isFloatingWindowEnable = controller.isFloatingWindowEnable.value;
+      // 启用了键盘,但悬浮窗权限未获取到,则高亮按钮
+      bool isHighlightBtn = isKeyboardEnable && !isFloatingWindowEnable;
+      return Container(
+        margin: EdgeInsets.symmetric(horizontal: 50.w),
+        child: GradientTextBtn(
+          enable: isHighlightBtn,
+          StringName.tutorialEnableFloatingBallStep,
+          onPressed: () {
+            // 已获取,不响应点击
+            if (controller.isFloatingWindowEnable.value) {
+              return;
+            }
+            // 去启用悬浮球
+            controller.jumpFloatingWindowSetting();
+          },
+        ),
+      );
+    });
+  }
+
+  /// 底部操作按钮
+  Widget _buildBottomActionBtn() {
+    return Column(
+      children: [
+        // 启用键盘
+        _buildEnableKeyboard(),
+        SizedBox(height: 20.h),
+        // 启用悬浮球
+        _buildFloatingBallActionBtn(),
+        // 距离底部一定距离
+        SizedBox(height: 25.h),
+      ],
+    );
+  }
+}

+ 89 - 0
lib/module/keyboard_tutorial/android/keyboard_tutorial_android_view_controller.dart

@@ -0,0 +1,89 @@
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:keyboard/module/keyboard_guide/keyboard_guide_page.dart';
+import 'package:keyboard/module/keyboard_tutorial/video/keyboard_tutorial_video_page.dart';
+
+import '../../../plugins/keyboard_android_platform.dart';
+import '../../../utils/floating_window_helper.dart';
+import '../../../utils/keyboard_tutorial_util.dart';
+
+/// 键盘教程页-Android平台Controller
+@injectable
+class KeyboardTutorialAndroidViewController extends BaseController {
+  /// 键盘是否启用
+  Rx<bool> isKeyboardEnable = false.obs;
+
+  /// 悬浮球是否启用
+  Rx<bool> isFloatingWindowEnable = false.obs;
+
+  /// 是否步骤都允许了
+  bool get isStepComplete =>
+      isKeyboardEnable.value && isFloatingWindowEnable.value;
+
+  @override
+  void onInit() {
+    super.onInit();
+    checkSetting();
+  }
+
+  /// 检查设置
+  void checkSetting() {
+    // 检查键盘是否启用
+    KeyboardAndroidPlatform.isTargetKeyboardEnabled().then((enable) {
+      isKeyboardEnable.value = enable;
+    });
+    // 检查是否有悬浮窗权限
+    KeyboardAndroidPlatform.hasFloatingWindowPermission().then((hasPermission) {
+      isFloatingWindowEnable.value = hasPermission;
+    });
+    // 有权限,则开启悬浮窗
+    FloatingWindowHelper.showFloatingBall();
+  }
+
+  /// 点击返回
+  void clickBack() {
+    Get.back();
+  }
+
+  /// 点击跳过
+  void clickSkip() {
+    // 标记,已经完成了键盘引导
+    KeyboardTutorialUtil.setNotFirstShowKeyboardTutorial();
+    // 跳转到键盘引导页,并关闭自己
+    KeyboardGuidePage.startAndOffMe();
+  }
+
+  /// 跳转去视频教程引导页
+  void clickTutorialVideo() {
+    KeyboardTutorialVideoPage.start();
+  }
+
+  /// 去启用键盘
+  void goEnableKeyboard() {
+    KeyboardAndroidPlatform.openInputMethodSettings();
+  }
+
+  /// 去启用悬浮窗
+  void jumpFloatingWindowSetting() {
+    KeyboardAndroidPlatform.jumpFloatingWindowSetting();
+  }
+
+  /// 跳转到键盘引导页
+  void jump2KeyboardGuide() {
+    // 标记,已经完成了键盘引导
+    KeyboardTutorialUtil.setNotFirstShowKeyboardTutorial();
+    // 跳转,并关闭自己
+    KeyboardGuidePage.startAndOffMe();
+  }
+
+  /// 检查,是否步骤都完成了
+  Future<bool> checkHasComplete() async {
+    // 都完成了,则跳转到键盘引导页
+    if (await KeyboardTutorialUtil.isTutorialComplete()) {
+      jump2KeyboardGuide();
+      return true;
+    }
+    return false;
+  }
+}

+ 207 - 0
lib/module/keyboard_tutorial/ios/keyboard_tutorial_ios_view.dart

@@ -0,0 +1,207 @@
+import 'package:flutter/Material.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
+import 'package:keyboard/base/base_view.dart';
+import '../../../resource/assets.gen.dart';
+import '../../../resource/colors.gen.dart';
+import '../../../resource/string.gen.dart';
+import '../../../utils/status_bar_util.dart';
+import '../../../widget/app_lifecycle_widget.dart';
+import '../../../widget/gradient_btn.dart';
+import '../../../widget/status_bar_placeholder_widget.dart';
+import '../../../widget/top_bar.dart';
+import '../widget/tutorial_header_widget.dart';
+import 'keyboard_tutorial_ios_view_controller.dart';
+
+class KeyboardTutorialIosView
+    extends BaseView<KeyboardTutorialIosViewController> {
+  const KeyboardTutorialIosView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return AppLifecycleWidget(
+      onAppLifecycleCallback: (isForeground) async {},
+      child: Stack(
+        children: [
+          // 头部
+          Positioned(left: 0, top: 0, right: 0, child: _buildHeader(context)),
+          // 状态栏和标题栏
+          Positioned(
+            left: 0,
+            top: 0,
+            right: 0,
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: [StatusBarPlaceholderWidget(), _buildTopBar()],
+            ),
+          ),
+          // 内容,填充剩余部分
+          Positioned.fill(
+            top: 170.h + StatusBarUtil.getStatusBarHeight(Get.context!),
+            left: 0,
+            right: 0,
+            child: Column(children: [_buildContent()]),
+          ),
+          // 底部按钮
+          Positioned(
+            left: 0,
+            right: 0,
+            bottom: 0,
+            child: _buildBottomActionBtn(),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 顶部栏
+  Widget _buildTopBar() {
+    return TopBar(
+      // 返回按钮
+      leftWidget: GestureDetector(
+        onTap: controller.clickBack,
+        child: Assets.images.iconBlackBackArrow.image(
+          width: 24.w,
+          height: 24.h,
+        ),
+      ),
+      // 视频教程按钮
+      rightWidget: Positioned(
+        right: 16.w,
+        child: Visibility(
+          visible: true,
+          child: GestureDetector(
+            onTap: () {
+              controller.clickTutorialVideo();
+            },
+            child: Text(
+              StringName.keyboardTutorialVideo,
+              style: TextStyle(
+                color: ColorName.black85,
+                fontSize: 14.sp,
+                fontWeight: FontWeight.w500,
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 头部布局
+  Widget _buildHeader(BuildContext context) {
+    return TutorialHeaderWidget();
+  }
+
+  /// 内容
+  Widget _buildContent() {
+    return Expanded(child: Column(children: [_buildTutorialList()]));
+  }
+
+  /// 键盘设置教程列表
+  Widget _buildTutorialList() {
+    return Column(
+      children: [
+        _buildTutorialStep1(),
+        SizedBox(height: 14.h),
+        _buildTutorialStep2(),
+      ],
+    );
+  }
+
+  /// 步骤1
+  Widget _buildTutorialStep1() {
+    return _buildTutorialStep(
+      StringName.tutorialEnableKeyboardIosStep1,
+      Assets.images.keyboardTutorialIosStep1.image(width: 296.w, height: 152.h),
+    );
+  }
+
+  /// 步骤2
+  Widget _buildTutorialStep2() {
+    return _buildTutorialStep(
+      StringName.tutorialEnableKeyboardIosStep2,
+      Assets.images.keyboardTutorialIosStep2.image(width: 296.w, height: 136.h),
+    );
+  }
+
+  /// 步骤
+  Widget _buildTutorialStep(String title, Image tutorialImage) {
+    return _buildStepBg(
+      child: Padding(
+        padding: EdgeInsets.all(16.w),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            // 标题
+            Text(
+              title,
+              style: TextStyle(
+                color: ColorName.black80,
+                fontWeight: FontWeight.w700,
+                fontSize: 14.sp,
+              ),
+            ),
+            SizedBox(height: 18.h),
+            // 示意图
+            tutorialImage,
+          ],
+        ),
+      ),
+    );
+  }
+
+  /// 步骤背景
+  Widget _buildStepBg({required Widget child}) {
+    return Container(
+      decoration: BoxDecoration(
+        color: ColorName.white,
+        borderRadius: BorderRadius.circular(20.r),
+        boxShadow: [
+          BoxShadow(
+            // x、y轴的偏移量
+            offset: Offset(0, 2),
+            // 模糊半径
+            blurRadius: 10,
+            // 阴影颜色
+            color: Color(0x4FEBE7FA),
+          ),
+        ],
+      ),
+      child: child,
+    );
+  }
+
+  /// 去开启键盘按钮
+  Widget _buildEnableKeyboard() {
+    return Container(
+      margin: EdgeInsets.symmetric(horizontal: 50.w),
+      child: GradientTextBtn(
+        StringName.tutorialEnableKeyboardIos,
+        onPressed: () {
+          controller.goEnableKeyboard();
+        },
+      ),
+    );
+  }
+
+  /// 底部操作按钮
+  Widget _buildBottomActionBtn() {
+    return Column(
+      children: [
+        // 去开启键盘按钮
+        _buildEnableKeyboard(),
+        // 距离底部一定距离
+        SizedBox(height: 52.h),
+      ],
+    );
+  }
+}

+ 33 - 0
lib/module/keyboard_tutorial/ios/keyboard_tutorial_ios_view_controller.dart

@@ -0,0 +1,33 @@
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+
+import '../../../base/base_controller.dart';
+import '../../../utils/url_launcher_util.dart';
+import '../video/keyboard_tutorial_video_page.dart';
+
+/// 键盘教程页-iOS平台Controller
+@injectable
+class KeyboardTutorialIosViewController extends BaseController {
+  final String _tag = "KeyboardTutorialIosViewController";
+
+  /// 点击返回
+  void clickBack() {
+    Get.back();
+  }
+
+  /// 跳转去视频教程引导页
+  void clickTutorialVideo() {
+    KeyboardTutorialVideoPage.start();
+  }
+
+  /// 去开启键盘
+  void goEnableKeyboard() async {
+    bool result = await UrlLauncherUtil.openIosSystemKeyboardPage();
+    if (result) {
+      AtmobLog.d(_tag, "打开iOS系统键盘设置页 => 成功");
+    } else {
+      AtmobLog.d(_tag, "打开iOS系统键盘设置页 => 失败");
+    }
+  }
+}

+ 0 - 82
lib/module/keyboard_tutorial/keyboard_tutorial_controller.dart

@@ -1,89 +1,7 @@
-import 'package:get/get.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
-import 'package:keyboard/module/keyboard_guide/keyboard_guide_page.dart';
-import 'package:keyboard/module/keyboard_tutorial/video/keyboard_tutorial_video_page.dart';
-
-import '../../plugins/keyboard_android_platform.dart';
-import '../../utils/floating_window_helper.dart';
-import '../../utils/keyboard_tutorial_util.dart';
 
 /// 键盘使用教程-引导页Controller
 @injectable
 class KeyboardTutorialController extends BaseController {
-  /// 键盘是否启用
-  Rx<bool> isKeyboardEnable = false.obs;
-
-  /// 悬浮球是否启用
-  Rx<bool> isFloatingWindowEnable = false.obs;
-
-  /// 是否步骤都允许了
-  bool get isStepComplete =>
-      isKeyboardEnable.value && isFloatingWindowEnable.value;
-
-  @override
-  void onInit() {
-    super.onInit();
-    checkSetting();
-  }
-
-  /// 检查设置
-  void checkSetting() {
-    // 检查键盘是否启用
-    KeyboardAndroidPlatform.isTargetKeyboardEnabled().then((enable) {
-      isKeyboardEnable.value = enable;
-    });
-    // 检查是否有悬浮窗权限
-    KeyboardAndroidPlatform.hasFloatingWindowPermission().then((hasPermission) {
-      isFloatingWindowEnable.value = hasPermission;
-    });
-    // 有权限,则开启悬浮窗
-    FloatingWindowHelper.showFloatingBall();
-  }
-
-  /// 点击返回
-  void clickBack() {
-    Get.back();
-  }
-
-  /// 点击跳过
-  void clickSkip() {
-    // 标记,已经完成了键盘引导
-    KeyboardTutorialUtil.setNotFirstShowKeyboardTutorial();
-    // 跳转到键盘引导页,并关闭自己
-    KeyboardGuidePage.startAndOffMe();
-  }
-
-  /// 跳转去视频教程引导页
-  void clickTutorialVideo() {
-    KeyboardTutorialVideoPage.start();
-  }
-
-  /// 去启用键盘
-  void goEnableKeyboard() {
-    KeyboardAndroidPlatform.openInputMethodSettings();
-  }
-
-  /// 去启用悬浮窗
-  void jumpFloatingWindowSetting() {
-    KeyboardAndroidPlatform.jumpFloatingWindowSetting();
-  }
-
-  /// 跳转到键盘引导页
-  void jump2KeyboardGuide() {
-    // 标记,已经完成了键盘引导
-    KeyboardTutorialUtil.setNotFirstShowKeyboardTutorial();
-    // 跳转,并关闭自己
-    KeyboardGuidePage.startAndOffMe();
-  }
-
-  /// 检查,是否步骤都完成了
-  Future<bool> checkHasComplete() async {
-    // 都完成了,则跳转到键盘引导页
-    if (await KeyboardTutorialUtil.isTutorialComplete()) {
-      jump2KeyboardGuide();
-      return true;
-    }
-    return false;
-  }
 }

+ 11 - 202
lib/module/keyboard_tutorial/keyboard_tutorial_page.dart

@@ -1,17 +1,10 @@
+import 'dart:io';
 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/resource/colors.gen.dart';
-import 'package:keyboard/resource/string.gen.dart';
 import 'package:keyboard/router/app_pages.dart';
-import 'package:lottie/lottie.dart';
-
-import '../../resource/assets.gen.dart';
-import '../../utils/status_bar_util.dart';
-import '../../widget/app_lifecycle_widget.dart';
-import '../../widget/gradient_btn.dart';
-import '../../widget/top_bar.dart';
+import 'android/keyboard_tutorial_android_view.dart';
+import 'ios/keyboard_tutorial_ios_view.dart';
 import 'keyboard_tutorial_controller.dart';
 
 /// 键盘使用教程-引导页
@@ -40,201 +33,17 @@ class KeyboardTutorialPage extends BasePage<KeyboardTutorialController> {
 
   @override
   Widget buildBody(BuildContext context) {
-    return Scaffold(
-      backgroundColor: backgroundColor(),
-      body: AppLifecycleWidget(
-        onAppLifecycleCallback: (isForeground) async {
-          // 步骤都做完了,则跳转去键盘引导页
-          if (await controller.checkHasComplete()) {
-            return;
-          }
-          // 未完成,则每次切换到前台时,重新检查设置,更新按钮状态
-          if (isForeground) {
-            controller.checkSetting();
-          }
-        },
-        child: Stack(
-          children: [
-            // 头部
-            Positioned(left: 0, top: 0, right: 0, child: _buildHeader(context)),
-            // 状态栏和标题栏
-            Positioned(
-              left: 0,
-              top: 0,
-              right: 0,
-              child: Column(
-                mainAxisSize: MainAxisSize.min,
-                children: [_buildStatusBar(), _buildTopBar()],
-              ),
-            ),
-            // 内容,填充剩余部分
-            Positioned.fill(
-              top: 170.h + StatusBarUtil.getStatusBarHeight(Get.context!),
-              left: 0,
-              right: 0,
-              child: Column(children: [_buildContent()]),
-            ),
-            // 底部按钮
-            Positioned(
-              left: 0,
-              right: 0,
-              bottom: 0,
-              child: _buildBottomActionBtn(),
-            ),
-          ],
-        ),
-      ),
-    );
-  }
-
-  /// 导航栏占位
-  Widget _buildStatusBar() {
-    // 导航栏高度
-    double statusBarHeight = StatusBarUtil.getStatusBarHeight(Get.context!);
-    return Container(height: statusBarHeight);
-  }
-
-  /// 顶部栏
-  Widget _buildTopBar() {
-    return TopBar(
-      leftWidget: GestureDetector(
-        onTap: controller.clickBack,
-        child: Assets.images.iconBlackBackArrow.image(
-          width: 24.w,
-          height: 24.h,
-        ),
-      ),
-      rightWidget: // 视频教程按钮
-          Positioned(
-        right: 16.w,
-        child: Visibility(
-          visible: true,
-          child: GestureDetector(
-            onTap: () {
-              controller.clickTutorialVideo();
-            },
-            child: Text(
-              StringName.keyboardTutorialVideo,
-              style: TextStyle(
-                color: ColorName.black85,
-                fontSize: 14.sp,
-                fontWeight: FontWeight.w500,
-              ),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-
-  /// 头部布局
-  Widget _buildHeader(BuildContext context) {
-    return SizedBox(
-      width: double.infinity,
-      height: 298.h,
-      child: Stack(
-        alignment: Alignment.center,
-        children: [
-          // 头像
-          Positioned(
-            top: 37.5.h,
-            bottom: 86.5.h,
-            child: Assets.images.iconKeyboardTutorialHeader.image(
-              height: 174.h,
-              fit: BoxFit.cover,
-            ),
-          ),
-          // 蒙版
-          Assets.images.bgKeyboardTutorialHeaderMask.image(
-            width: double.infinity,
-            height: double.infinity,
-          ),
-          // 提示语
-          Positioned(
-            top: 70.h + StatusBarUtil.getStatusBarHeight(context),
-            child: Assets.images.iconKeyboardTutorialHeaderSlogan.image(
-              width: 200.w,
-              height: 73.h,
-            ),
-          ),
-        ],
-      ),
-    );
+    return Scaffold(backgroundColor: backgroundColor(), body: _buildContent());
   }
 
   /// 内容
   Widget _buildContent() {
-    return Expanded(child: Column(children: [_buildTutorialAnimation()]));
-  }
-
-  /// 键盘设置引导动画
-  Widget _buildTutorialAnimation() {
-    return Lottie.asset(
-      Assets.anim.animKeyboardTutorialSetting,
-      repeat: true,
-      width: 340.w,
-    );
-  }
-
-  /// 启用键盘按钮
-  Widget _buildEnableKeyboard() {
-    return Obx(() {
-      return Container(
-        margin: EdgeInsets.symmetric(horizontal: 50.w),
-        child: GradientTextBtn(
-          // 是否启用,这里UI设计上,未启用时要高亮按钮,所以要取反
-          enable: !controller.isKeyboardEnable.value,
-          StringName.tutorialEnableKeyboardStep,
-          onPressed: () {
-            // 已启用,不响应点击
-            if (controller.isKeyboardEnable.value) {
-              return;
-            }
-            // 去启用键盘
-            controller.goEnableKeyboard();
-          },
-        ),
-      );
-    });
-  }
-
-  /// 悬浮球设置按钮
-  Widget _buildFloatingBallActionBtn() {
-    return Obx(() {
-      bool isKeyboardEnable = controller.isKeyboardEnable.value;
-      bool isFloatingWindowEnable = controller.isFloatingWindowEnable.value;
-      // 启用了键盘,但悬浮窗权限未获取到,则高亮按钮
-      bool isHighlightBtn = isKeyboardEnable && !isFloatingWindowEnable;
-      return Container(
-        margin: EdgeInsets.symmetric(horizontal: 50.w),
-        child: GradientTextBtn(
-          enable: isHighlightBtn,
-          StringName.tutorialEnableFloatingBallStep,
-          onPressed: () {
-            // 已获取,不响应点击
-            if (controller.isFloatingWindowEnable.value) {
-              return;
-            }
-            // 去启用悬浮球
-            controller.jumpFloatingWindowSetting();
-          },
-        ),
-      );
-    });
-  }
-
-  /// 底部操作按钮
-  Widget _buildBottomActionBtn() {
-    return Column(
-      children: [
-        // 启用键盘
-        _buildEnableKeyboard(),
-        SizedBox(height: 20.h),
-        // 启用悬浮球
-        _buildFloatingBallActionBtn(),
-        // 距离底部一定距离
-        SizedBox(height: 25.h),
-      ],
-    );
+    Widget content;
+    if (Platform.isIOS) {
+      content = const KeyboardTutorialIosView();
+    } else {
+      content = const KeyboardTutorialAndroidView();
+    }
+    return content;
   }
 }

+ 45 - 0
lib/module/keyboard_tutorial/widget/tutorial_header_widget.dart

@@ -0,0 +1,45 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+
+import '../../../resource/assets.gen.dart';
+import '../../../utils/status_bar_util.dart';
+
+/// 键盘教程页的顶部组件
+class TutorialHeaderWidget extends StatelessWidget {
+  const TutorialHeaderWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      width: double.infinity,
+      height: 298.h,
+      child: Stack(
+        alignment: Alignment.center,
+        children: [
+          // 头像
+          Positioned(
+            top: 37.5.h,
+            bottom: 86.5.h,
+            child: Assets.images.iconKeyboardTutorialHeader.image(
+              height: 174.h,
+              fit: BoxFit.cover,
+            ),
+          ),
+          // 蒙版
+          Assets.images.bgKeyboardTutorialHeaderMask.image(
+            width: double.infinity,
+            height: double.infinity,
+          ),
+          // 提示语
+          Positioned(
+            top: 70.h + StatusBarUtil.getStatusBarHeight(context),
+            child: Assets.images.iconKeyboardTutorialHeaderSlogan.image(
+              width: 200.w,
+              height: 73.h,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

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

@@ -1239,6 +1239,14 @@ class $AssetsImagesGen {
   AssetGenImage get iconWhiteBackArrow =>
       const AssetGenImage('assets/images/icon_white_back_arrow.webp');
 
+  /// File path: assets/images/keyboard_tutorial_ios_step_1.webp
+  AssetGenImage get keyboardTutorialIosStep1 =>
+      const AssetGenImage('assets/images/keyboard_tutorial_ios_step_1.webp');
+
+  /// File path: assets/images/keyboard_tutorial_ios_step_2.webp
+  AssetGenImage get keyboardTutorialIosStep2 =>
+      const AssetGenImage('assets/images/keyboard_tutorial_ios_step_2.webp');
+
   /// List of all assets
   List<AssetGenImage> get values => [
     bgAgreementAgainDialog,
@@ -1516,6 +1524,8 @@ class $AssetsImagesGen {
     iconWechatPayment,
     iconWechatScanPayment,
     iconWhiteBackArrow,
+    keyboardTutorialIosStep1,
+    keyboardTutorialIosStep2,
   ];
 }
 

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

@@ -171,6 +171,9 @@ class StringName {
   static final String keyboardTutorialVideo = 'keyboard_tutorial_video'.tr; // 视频教程
   static final String tutorialEnableKeyboardStep = 'tutorial_enable_keyboard_step'.tr; // 1.启用键盘
   static final String tutorialEnableFloatingBallStep = 'tutorial_enable_floating_ball_step'.tr; // 2.开启悬浮球
+  static final String tutorialEnableKeyboardIos = 'tutorial_enable_keyboard_ios'.tr; // 开启追爱小键盘
+  static final String tutorialEnableKeyboardIosStep1 = 'tutorial_enable_keyboard_ios_step1'.tr; // 1.点击[键盘]
+  static final String tutorialEnableKeyboardIosStep2 = 'tutorial_enable_keyboard_ios_step2'.tr; // 2.开启【追爱小键盘】,并【允许完全访问】
   static final String profileSave = 'profile_save'.tr; // 完成
   static final String profileEditSave = 'profile_edit_save'.tr; // 保存
   static final String profileList = 'profile_list'.tr; // 档案列表
@@ -521,6 +524,9 @@ class StringMultiSource {
       'keyboard_tutorial_video': '视频教程',
       'tutorial_enable_keyboard_step': '1.启用键盘',
       'tutorial_enable_floating_ball_step': '2.开启悬浮球',
+      'tutorial_enable_keyboard_ios': '开启追爱小键盘',
+      'tutorial_enable_keyboard_ios_step1': '1.点击[键盘]',
+      'tutorial_enable_keyboard_ios_step2': '2.开启【追爱小键盘】,并【允许完全访问】',
       'profile_save': '完成',
       'profile_edit_save': '保存',
       'profile_list': '档案列表',

+ 4 - 0
lib/router/app_pages.dart

@@ -63,6 +63,8 @@ import '../module/intro/intro_page.dart';
 import '../module/keyboard_guide/keyboard_guide_controller.dart';
 import '../module/keyboard_guide/keyboard_guide_page.dart';
 import '../module/keyboard_manage/keyboard_manage_page.dart';
+import '../module/keyboard_tutorial/android/keyboard_tutorial_android_view_controller.dart';
+import '../module/keyboard_tutorial/ios/keyboard_tutorial_ios_view_controller.dart';
 import '../module/keyboard_tutorial/keyboard_tutorial_controller.dart';
 import '../module/keyboard_tutorial/keyboard_tutorial_page.dart';
 import '../module/login/login_page.dart';
@@ -187,6 +189,8 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<ZodiacLoveIntimacyController>());
     lazyPut(() => getIt.get<IntimacyScaleController>());
     lazyPut(() => getIt.get<KeyboardTutorialController>());
+    lazyPut(() => getIt.get<KeyboardTutorialAndroidViewController>());
+    lazyPut(() => getIt.get<KeyboardTutorialIosViewController>());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {

+ 11 - 0
lib/utils/url_launcher_util.dart

@@ -5,6 +5,17 @@ class UrlLauncherUtil {
   /// 打开微信
   static Future<bool> openWeChat() async {
     const url = 'weixin://';
+    return openUrl(url);
+  }
+
+  /// 打开iOS系统键盘设置页
+  static openIosSystemKeyboardPage() {
+    const url = 'App-Prefs:root=General&path=Keyboard';
+    return openUrl(url);
+  }
+
+  /// 打开Url
+  static Future<bool> openUrl(String url) async {
     if (!await launchUrl(Uri.parse(url))) {
       return Future.value(false);
     }

+ 15 - 0
lib/widget/status_bar_placeholder_widget.dart

@@ -0,0 +1,15 @@
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import '../utils/status_bar_util.dart';
+
+/// 状态栏占位组件
+class StatusBarPlaceholderWidget extends StatelessWidget {
+  const StatusBarPlaceholderWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    // 导航栏高度
+    double statusBarHeight = StatusBarUtil.getStatusBarHeight(Get.context!);
+    return Container(height: statusBarHeight);
+  }
+}