소스 검색

[feat]增加视频教程页,但还有问题,先隐藏

hezihao 7 달 전
부모
커밋
82eedc26eb

BIN
assets/images/bg_keyboard_video_tutorial_header_mask.webp


BIN
assets/images/icon_keyboard_tutorial_video_header_slogan.webp


BIN
assets/images/icon_pause.webp


BIN
assets/images/icon_play.webp


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

@@ -229,6 +229,8 @@
     <string name="discount_dialog_character">百种人设</string>
     <string name="discount_dialog_social">扩大社交</string>
 
+    <string name="keyboard_tutorial_video">视频教程</string>
+
     <string name="tutorial_enable_keyboard_step">1.启用键盘</string>
     <string name="tutorial_enable_floating_ball_step">2.开启悬浮球</string>
 

BIN
assets/videos/keyboard_tutorial_video.mp4


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

@@ -71,6 +71,8 @@ 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/keyboard_tutorial_controller.dart' as _i507;
+import '../module/keyboard_tutorial/video/keyboard_tutorial_video_controller.dart'
+    as _i101;
 import '../module/login/login_controller.dart' as _i1008;
 import '../module/main/main_controller.dart' as _i731;
 import '../module/mine/mine_controller.dart' as _i732;
@@ -139,6 +141,9 @@ extension GetItInjectableX on _i174.GetIt {
     gh.factory<_i415.KeyboardMethodHandler>(
       () => _i415.KeyboardMethodHandler(),
     );
+    gh.factory<_i101.KeyboardTutorialVideoController>(
+      () => _i101.KeyboardTutorialVideoController(),
+    );
     gh.lazySingleton<_i495.WechatLoginService>(
       () => _i495.WechatLoginService(),
     );

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

@@ -2,6 +2,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';
@@ -53,6 +54,11 @@ class KeyboardTutorialController extends BaseController {
     KeyboardGuidePage.startAndOffMe();
   }
 
+  /// 跳转去视频教程引导页
+  void clickTutorialVideo() {
+    KeyboardTutorialVideoPage.start();
+  }
+
   /// 去启用键盘
   void goEnableKeyboard() {
     KeyboardAndroidPlatform.openInputMethodSettings();

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

@@ -119,19 +119,28 @@ class KeyboardTutorialPage extends BasePage<KeyboardTutorialController> {
                 ),
               ),
             ),
-            // 跳过按钮
+            // 视频教程按钮
             Positioned(
               right: 16.w,
-              child: GestureDetector(
-                onTap: () {
-                  controller.clickSkip();
-                },
-                child: Text(
-                  StringName.skip,
-                  style: TextStyle(
-                    color: ColorName.black85,
-                    fontSize: 14.sp,
-                    fontWeight: FontWeight.w500,
+              child: Visibility(
+                visible: false,
+                child: GestureDetector(
+                  onTap: () {
+                    controller.clickTutorialVideo();
+                  },
+                  child: Text(
+                    StringName.keyboardTutorialVideo,
+                    style: TextStyle(
+                      color: ColorName.black85,
+                      fontSize: 14.sp,
+                      fontWeight: FontWeight.w500,
+                      // 下划线
+                      decoration: TextDecoration.underline,
+                      // 下划线颜色
+                      decorationColor: ColorName.black85,
+                      // 下划线粗细
+                      decorationThickness: 1.0,
+                    ),
                   ),
                 ),
               ),

+ 68 - 0
lib/module/keyboard_tutorial/video/keyboard_tutorial_video_controller.dart

@@ -0,0 +1,68 @@
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:video_player/video_player.dart';
+
+/// 键盘使用教程-视频引导页Controller
+@injectable
+class KeyboardTutorialVideoController extends BaseController {
+  /// 视频是否初始化
+  final Rx<bool> _isVideoInitialized = false.obs;
+
+  /// 视频是否正在播放
+  final Rx<bool> isVideoPlaying = false.obs;
+
+  /// 视频控制器
+  late VideoPlayerController videoController;
+
+  @override
+  void onInit() {
+    super.onInit();
+    _initVideoController();
+  }
+
+  @override
+  void onClose() {
+    // 移除视频状态监听器
+    videoController.removeListener(_updatePlayingStatus);
+    // 释放资源
+    videoController.dispose();
+    super.onClose();
+  }
+
+  /// 初始化视频播放器
+  void _initVideoController() {
+    videoController = VideoPlayerController.asset(
+        'assets/videos/keyboard_tutorial_video.mp4',
+      )
+      ..initialize().then((_) {
+        _isVideoInitialized.value = true;
+        // 视频初始化完成后,自动播放
+        videoController.play();
+      });
+    // 设置循环播放
+    videoController.setLooping(true);
+    // 添加播放状态监听器
+    videoController.addListener(_updatePlayingStatus);
+  }
+
+  /// 更新视频的播放状态
+  void _updatePlayingStatus() {
+    isVideoPlaying.value = videoController.value.isPlaying;
+  }
+
+  /// 返回
+  void clickBack() {
+    Get.back();
+  }
+
+  /// 播放
+  void clickPlay() {
+    videoController.play();
+  }
+
+  /// 暂停
+  void clickPause() {
+    videoController.pause();
+  }
+}

+ 219 - 0
lib/module/keyboard_tutorial/video/keyboard_tutorial_video_page.dart

@@ -0,0 +1,219 @@
+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/assets.gen.dart';
+import 'package:keyboard/resource/colors.gen.dart';
+import 'package:video_player/video_player.dart';
+
+import '../../../di/get_it.dart';
+import '../../../router/app_pages.dart';
+import '../../../utils/status_bar_util.dart';
+import 'keyboard_tutorial_video_controller.dart';
+
+/// 键盘使用教程-视频引导页
+class KeyboardTutorialVideoPage
+    extends BasePage<KeyboardTutorialVideoController> {
+  KeyboardTutorialVideoPage({super.key}) {
+    Get.lazyPut(() => getIt.get<KeyboardTutorialVideoController>());
+  }
+
+  static void start() {
+    Get.toNamed(RoutePath.keyboardTutorialVideo);
+  }
+
+  @override
+  bool immersive() {
+    // 开启沉浸式
+    return true;
+  }
+
+  @override
+  backgroundColor() {
+    return Color(0xFFF2EFFF);
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Scaffold(
+      backgroundColor: backgroundColor(),
+      body: 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: 180.h + StatusBarUtil.getStatusBarHeight(Get.context!),
+            left: 0,
+            right: 0,
+            bottom: 0,
+            child: SingleChildScrollView(child: _buildContent()),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 导航栏占位
+  Widget _buildStatusBar() {
+    // 导航栏高度
+    double statusBarHeight = StatusBarUtil.getStatusBarHeight(Get.context!);
+    return Container(height: statusBarHeight);
+  }
+
+  /// 顶部栏
+  Widget _buildTopBar() {
+    return Container(
+      // 宽度撑满父组件
+      width: double.infinity,
+      height: kToolbarHeight,
+      // 背景颜色
+      color: Colors.transparent,
+      // padding: EdgeInsets.symmetric(horizontal: 16.0),
+      child: ConstrainedBox(
+        // 设置宽度为无限大,撑满父组件,否则Stack获取不到高度,会报错
+        constraints: BoxConstraints(minWidth: double.infinity),
+        child: Stack(
+          alignment: Alignment.center,
+          children: [
+            // 返回按钮
+            Positioned(
+              left: 16.w,
+              child: GestureDetector(
+                onTap: controller.clickBack,
+                child: Assets.images.iconBlackBackArrow.image(
+                  width: 24.w,
+                  height: 24.h,
+                ),
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  /// 头部布局
+  Widget _buildHeader(BuildContext context) {
+    return SizedBox(
+      width: double.infinity,
+      height: 260.h,
+      child: Stack(
+        alignment: Alignment.center,
+        children: [
+          // 蒙版
+          Assets.images.bgKeyboardVideoTutorialHeaderMask.image(
+            width: double.infinity,
+            height: double.infinity,
+          ),
+          // 提示语
+          Positioned(
+            top: 20.h + StatusBarUtil.getStatusBarHeight(context),
+            child: Assets.images.iconKeyboardTutorialVideoHeaderSlogan.image(
+              width: 352.w,
+              height: 178.h,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 内容
+  Widget _buildContent() {
+    return Container(
+      // 渐变背景
+      decoration: BoxDecoration(
+        gradient: LinearGradient(
+          colors: [Color(0x00FFFFFF), Color(0xFFF2EFFF)],
+          begin: Alignment.topCenter,
+          end: Alignment.bottomRight,
+        ),
+        shape: BoxShape.rectangle,
+      ),
+      child: Column(children: [_buildTutorialVideo()]),
+    );
+  }
+
+  /// 教程视频
+  Widget _buildTutorialVideo() {
+    return Container(
+      margin: EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 23),
+      padding: EdgeInsets.all(12.w),
+      decoration: BoxDecoration(
+        color: ColorName.white,
+        borderRadius: BorderRadius.all(Radius.circular(24.w)),
+      ),
+      child: LayoutBuilder(
+        builder: (context, constraints) {
+          double maxWidth = constraints.maxWidth - 24.w;
+          double videoRatio = controller.videoController.value.aspectRatio;
+          return Stack(
+            alignment: Alignment.center,
+            children: [
+              // 视频容器(动态宽高 + 裁剪)
+              SizedBox(
+                width: maxWidth,
+                height: maxWidth / videoRatio,
+                child: ClipRRect(
+                  borderRadius: BorderRadius.circular(12.w),
+                  child: VideoPlayer(controller.videoController),
+                ),
+              ),
+              // 操作按钮
+              _buildActionBtn(),
+            ],
+          );
+        },
+      ),
+    );
+  }
+
+  /// 操作按钮
+  Widget _buildActionBtn() {
+    return Obx(() {
+      return Stack(
+        children: [
+          Visibility(
+            visible: !controller.isVideoPlaying.value,
+            child: _buildPlayBtn(),
+          ),
+          Visibility(
+            visible: controller.isVideoPlaying.value,
+            child: _buildPauseBtn(),
+          ),
+        ],
+      );
+    });
+  }
+
+  /// 播放按钮
+  Widget _buildPlayBtn() {
+    return GestureDetector(
+      onTap: () {
+        controller.clickPlay();
+      },
+      child: Assets.images.iconPlay.image(width: 52.w, height: 52.w),
+    );
+  }
+
+  /// 暂停按钮
+  Widget _buildPauseBtn() {
+    return GestureDetector(
+      onTap: () {
+        controller.clickPause();
+      },
+      child: Assets.images.iconPause.image(width: 52.w, height: 52.w),
+    );
+  }
+}

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

@@ -227,6 +227,11 @@ class $AssetsImagesGen {
     'assets/images/bg_keyboard_tutorial_header_mask.webp',
   );
 
+  /// File path: assets/images/bg_keyboard_video_tutorial_header_mask.webp
+  AssetGenImage get bgKeyboardVideoTutorialHeaderMask => const AssetGenImage(
+    'assets/images/bg_keyboard_video_tutorial_header_mask.webp',
+  );
+
   /// File path: assets/images/bg_login.webp
   AssetGenImage get bgLogin =>
       const AssetGenImage('assets/images/bg_login.webp');
@@ -813,6 +818,12 @@ class $AssetsImagesGen {
     'assets/images/icon_keyboard_tutorial_header_slogan.webp',
   );
 
+  /// File path: assets/images/icon_keyboard_tutorial_video_header_slogan.webp
+  AssetGenImage get iconKeyboardTutorialVideoHeaderSlogan =>
+      const AssetGenImage(
+        'assets/images/icon_keyboard_tutorial_video_header_slogan.webp',
+      );
+
   /// File path: assets/images/icon_keyboard_vip_logo.webp
   AssetGenImage get iconKeyboardVipLogo =>
       const AssetGenImage('assets/images/icon_keyboard_vip_logo.webp');
@@ -1006,10 +1017,18 @@ class $AssetsImagesGen {
   AssetGenImage get iconNewUserZodiacRight =>
       const AssetGenImage('assets/images/icon_new_user_zodiac_right.webp');
 
+  /// File path: assets/images/icon_pause.webp
+  AssetGenImage get iconPause =>
+      const AssetGenImage('assets/images/icon_pause.webp');
+
   /// File path: assets/images/icon_pisces.webp
   AssetGenImage get iconPisces =>
       const AssetGenImage('assets/images/icon_pisces.webp');
 
+  /// File path: assets/images/icon_play.webp
+  AssetGenImage get iconPlay =>
+      const AssetGenImage('assets/images/icon_play.webp');
+
   /// File path: assets/images/icon_profile_add.webp
   AssetGenImage get iconProfileAdd =>
       const AssetGenImage('assets/images/icon_profile_add.webp');
@@ -1256,6 +1275,7 @@ class $AssetsImagesGen {
     bgKeyboardManageIntimacy,
     bgKeyboardScreenshotReply,
     bgKeyboardTutorialHeaderMask,
+    bgKeyboardVideoTutorialHeaderMask,
     bgLogin,
     bgLoginDialog,
     bgMine,
@@ -1394,6 +1414,7 @@ class $AssetsImagesGen {
     iconKeyboardTriangle,
     iconKeyboardTutorialHeader,
     iconKeyboardTutorialHeaderSlogan,
+    iconKeyboardTutorialVideoHeaderSlogan,
     iconKeyboardVipLogo,
     iconLeo,
     iconLibra,
@@ -1440,7 +1461,9 @@ class $AssetsImagesGen {
     iconNewUserStagesAmbiguous,
     iconNewUserZodiacLeft,
     iconNewUserZodiacRight,
+    iconPause,
     iconPisces,
+    iconPlay,
     iconProfileAdd,
     iconProfileEdit,
     iconProfileFemale,
@@ -1496,11 +1519,23 @@ class $AssetsImagesGen {
   ];
 }
 
+class $AssetsVideosGen {
+  const $AssetsVideosGen();
+
+  /// File path: assets/videos/keyboard_tutorial_video.mp4
+  String get keyboardTutorialVideo =>
+      'assets/videos/keyboard_tutorial_video.mp4';
+
+  /// List of all assets
+  List<String> get values => [keyboardTutorialVideo];
+}
+
 class Assets {
   const Assets._();
 
   static const $AssetsAnimGen anim = $AssetsAnimGen();
   static const $AssetsImagesGen images = $AssetsImagesGen();
+  static const $AssetsVideosGen videos = $AssetsVideosGen();
 }
 
 class AssetGenImage {

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

@@ -168,6 +168,7 @@ class StringName {
   static final String discountDialogTutorial = 'discount_dialog_tutorial'.tr; // 恋爱教程
   static final String discountDialogCharacter = 'discount_dialog_character'.tr; // 百种人设
   static final String discountDialogSocial = 'discount_dialog_social'.tr; // 扩大社交
+  static final String 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 profileSave = 'profile_save'.tr; // 完成
@@ -510,6 +511,7 @@ class StringMultiSource {
       'discount_dialog_tutorial': '恋爱教程',
       'discount_dialog_character': '百种人设',
       'discount_dialog_social': '扩大社交',
+      'keyboard_tutorial_video': '视频教程',
       'tutorial_enable_keyboard_step': '1.启用键盘',
       'tutorial_enable_floating_ball_step': '2.开启悬浮球',
       'profile_save': '完成',

+ 9 - 3
lib/router/app_pages.dart

@@ -18,6 +18,8 @@ import 'package:keyboard/module/intimacy_analyse/intimacy_analyse_page.dart';
 import 'package:keyboard/module/intimacy_scale/intimacy_scale_controller.dart';
 import 'package:keyboard/module/keyboard/keyboard_controller.dart';
 import 'package:keyboard/module/keyboard_manage/keyboard_manage_controller.dart';
+import 'package:keyboard/module/keyboard_tutorial/video/keyboard_tutorial_video_controller.dart';
+import 'package:keyboard/module/keyboard_tutorial/video/keyboard_tutorial_video_page.dart';
 import 'package:keyboard/module/login/login_controller.dart';
 import 'package:keyboard/module/mine/mine_controller.dart';
 import 'package:keyboard/module/new_user/new_user_controller.dart';
@@ -127,11 +129,14 @@ abstract class RoutePath {
   //   介绍页
   static const intro = '/intro';
 
-//   亲密度刻度调节
+  //   亲密度刻度调节
   static const intimacyScale = '/intimacyScale';
 
   // 键盘使用教程引导页
   static const keyboardTutorial = '/keyboardTutorial';
+
+  // 键盘使用教程引导-视频教程页
+  static const keyboardTutorialVideo = '/keyboardTutorialVideo';
 }
 
 class AppBinding extends Bindings {
@@ -239,8 +244,9 @@ final generalPages = [
   ),
   GetPage(name: RoutePath.intro, page: () => IntroPage()),
   GetPage(name: RoutePath.intimacyScale, page: () => IntimacyScalePage()),
+  GetPage(name: RoutePath.keyboardTutorial, page: () => KeyboardTutorialPage()),
   GetPage(
-    name: RoutePath.keyboardTutorial,
-    page: () => KeyboardTutorialPage(),
+    name: RoutePath.keyboardTutorialVideo,
+    page: () => KeyboardTutorialVideoPage(),
   ),
 ];

+ 19 - 0
lib/utils/video_player_config_util.dart

@@ -0,0 +1,19 @@
+// import 'package:flutter/material.dart';
+// import 'package:video_player_android/video_player_android.dart';
+//
+// /// 视频播放器配置工具类
+// class VideoPlayerConfigUtil {
+//   VideoPlayerConfigUtil._();
+//
+//   /// 配置视频播放器
+//   static void config() {
+//     AndroidVideoPlayer.registerWith = (callback) {
+//       final exoPlayer = AndroidExoPlayer(
+//         // 强制使用软件解码器
+//         httpClient: AndroidExoPlayerHttpClient(),
+//         codecFactory: AndroidCodecFactory(forceSoftwareCodecs: true), // <-- 关键配置
+//       );
+//       return AndroidVideoPlayer(exoPlayer);
+//     };
+//   }
+// }

+ 3 - 3
pubspec.lock

@@ -1556,13 +1556,13 @@ packages:
     source: hosted
     version: "2.1.4"
   video_player:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: video_player
-      sha256: "48941c8b05732f9582116b1c01850b74dbee1d8520cd7e34ad4609d6df666845"
+      sha256: "7d78f0cfaddc8c19d4cb2d3bebe1bfef11f2103b0a03e5398b303a1bf65eeb14"
       url: "https://pub.dev"
     source: hosted
-    version: "2.9.3"
+    version: "2.9.5"
   video_player_android:
     dependency: transitive
     description:

+ 3 - 1
pubspec.yaml

@@ -94,7 +94,8 @@ dependencies:
   #bugly
   flutter_bugly: 1.1.0
 
-
+  # 视频播放
+  video_player: ^2.9.5
 
   #QR
   qr_flutter: ^4.1.0
@@ -202,6 +203,7 @@ flutter:
   assets:
     - assets/anim/
     - assets/images/
+    - assets/videos/
 
 
 wechat_kit: