Browse Source

[feat]亲密度分析报告页,搭建UI

hezihao 7 tháng trước cách đây
mục cha
commit
64166b1133
27 tập tin đã thay đổi với 775 bổ sung6 xóa
  1. 1 0
      assets/color/common_color.xml
  2. BIN
      assets/images/bg_intimacy_analyse_report_preview.webp
  3. BIN
      assets/images/bg_intimacy_analyse_report_preview_bubble.webp
  4. BIN
      assets/images/bg_intimacy_analyse_report_preview_mask.webp
  5. BIN
      assets/images/icon_default_avatar_female.webp
  6. BIN
      assets/images/icon_default_avatar_male.webp
  7. BIN
      assets/images/icon_intimacy_analyse_arrow.webp
  8. BIN
      assets/images/icon_intimacy_analyse_love.webp
  9. BIN
      assets/images/icon_intimacy_analyse_report_preview_bubble_symbol.webp
  10. BIN
      assets/images/icon_intimacy_analyse_report_preview_love.webp
  11. BIN
      assets/images/icon_intimacy_analyse_report_preview_title.webp
  12. BIN
      assets/images/icon_intimacy_analyse_unlock.webp
  13. 35 0
      lib/data/model/intimacy_analyse_report.dart
  14. 33 0
      lib/data/model/intimacy_analyse_report.g.dart
  15. 10 0
      lib/di/get_it.config.dart
  16. 317 0
      lib/module/intimacy_analyse/analyse_report/intimacy_analyse_report_view.dart
  17. 47 0
      lib/module/intimacy_analyse/analyse_report/intimacy_analyse_report_view_controller.dart
  18. 36 0
      lib/module/intimacy_analyse/analyse_report/widget/report_item_widget.dart
  19. 43 0
      lib/module/intimacy_analyse/analyse_report/widget/report_section_widget.dart
  20. 22 0
      lib/module/intimacy_analyse/analyse_report/widget/report_title_widget.dart
  21. 12 6
      lib/module/intimacy_analyse/intimacy_analyse_page.dart
  22. 7 0
      lib/module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_controller.dart
  23. 21 0
      lib/module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_view.dart
  24. 63 0
      lib/resource/assets.gen.dart
  25. 3 0
      lib/resource/colors.gen.dart
  26. 4 0
      lib/router/app_pages.dart
  27. 121 0
      lib/widget/avatar/avatar_image_widget.dart

+ 1 - 0
assets/color/common_color.xml

@@ -31,6 +31,7 @@
     <color name="black80">#CC000000</color>
     <color name="black75">#B000000F</color>
     <color name="black70">#B3000000</color>
+    <color name="black72">#B8000000</color>
     <color name="black65">#A6000000</color>
     <color name="black60">#99000000</color>
     <color name="black55">#8C000000</color>

BIN
assets/images/bg_intimacy_analyse_report_preview.webp


BIN
assets/images/bg_intimacy_analyse_report_preview_bubble.webp


BIN
assets/images/bg_intimacy_analyse_report_preview_mask.webp


BIN
assets/images/icon_default_avatar_female.webp


BIN
assets/images/icon_default_avatar_male.webp


BIN
assets/images/icon_intimacy_analyse_arrow.webp


BIN
assets/images/icon_intimacy_analyse_love.webp


BIN
assets/images/icon_intimacy_analyse_report_preview_bubble_symbol.webp


BIN
assets/images/icon_intimacy_analyse_report_preview_love.webp


BIN
assets/images/icon_intimacy_analyse_report_preview_title.webp


BIN
assets/images/icon_intimacy_analyse_unlock.webp


+ 35 - 0
lib/data/model/intimacy_analyse_report.dart

@@ -0,0 +1,35 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'intimacy_analyse_report.g.dart';
+
+@JsonSerializable()
+class IntimacyAnalyseReport {
+  @JsonKey(name: 'list')
+  final List<AnalyseItem> list;
+
+  IntimacyAnalyseReport({required this.list});
+
+  factory IntimacyAnalyseReport.fromJson(Map<String, dynamic> json) =>
+      _$IntimacyAnalyseReportFromJson(json);
+
+  Map<String, dynamic> toJson() => _$IntimacyAnalyseReportToJson(this);
+}
+
+@JsonSerializable()
+class AnalyseItem {
+  @JsonKey(name: 'title')
+  final String title;
+
+  @JsonKey(name: 'sections')
+  final List<String> sections;
+
+  AnalyseItem({
+    required this.title,
+    required this.sections,
+  });
+
+  factory AnalyseItem.fromJson(Map<String, dynamic> json) =>
+      _$AnalyseSectionFromJson(json);
+
+  Map<String, dynamic> toJson() => _$AnalyseSectionToJson(this);
+}

+ 33 - 0
lib/data/model/intimacy_analyse_report.g.dart

@@ -0,0 +1,33 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'intimacy_analyse_report.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+IntimacyAnalyseReport _$IntimacyAnalyseReportFromJson(
+  Map<String, dynamic> json,
+) => IntimacyAnalyseReport(
+  list:
+      (json['list'] as List<dynamic>)
+          .map((e) => AnalyseItem.fromJson(e as Map<String, dynamic>))
+          .toList(),
+);
+
+Map<String, dynamic> _$IntimacyAnalyseReportToJson(
+  IntimacyAnalyseReport instance,
+) => <String, dynamic>{'list': instance.list};
+
+AnalyseItem _$AnalyseSectionFromJson(Map<String, dynamic> json) =>
+    AnalyseItem(
+      title: json['title'] as String,
+      sections:
+          (json['sections'] as List<dynamic>).map((e) => e as String).toList(),
+    );
+
+Map<String, dynamic> _$AnalyseSectionToJson(AnalyseItem instance) =>
+    <String, dynamic>{
+      'title': instance.title,
+      'sections': instance.sections,
+    };

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

@@ -38,7 +38,11 @@ import '../module/character_custom/detail/character_custom_detail_controller.dar
 import '../module/character_custom/list/character_custom_list_controller.dart'
     as _i1059;
 import '../module/feedback/feedback_controller.dart' as _i876;
+import '../module/intimacy_analyse/analyse_report/intimacy_analyse_report_view_controller.dart'
+    as _i987;
 import '../module/intimacy_analyse/intimacy_analyse_controller.dart' as _i977;
+import '../module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_controller.dart'
+    as _i279;
 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;
@@ -76,6 +80,12 @@ extension GetItInjectableX on _i174.GetIt {
     gh.factory<_i977.IntimacyAnalyseController>(
       () => _i977.IntimacyAnalyseController(),
     );
+    gh.factory<_i987.IntimacyAnalyseReportController>(
+      () => _i987.IntimacyAnalyseReportController(),
+    );
+    gh.factory<_i279.IntimacyAnalyseScreenshotReplyController>(
+      () => _i279.IntimacyAnalyseScreenshotReplyController(),
+    );
     gh.singleton<_i361.Dio>(
       () => networkModule.createStreamDio(),
       instanceName: 'streamDio',

+ 317 - 0
lib/module/intimacy_analyse/analyse_report/intimacy_analyse_report_view.dart

@@ -0,0 +1,317 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:keyboard/module/intimacy_analyse/analyse_report/widget/report_item_widget.dart';
+import 'package:keyboard/module/intimacy_analyse/analyse_report/widget/report_title_widget.dart';
+
+import '../../../data/model/intimacy_analyse_report.dart';
+import '../../../resource/assets.gen.dart';
+import '../../../resource/colors.gen.dart';
+import '../../../widget/avatar/avatar_image_widget.dart';
+import 'intimacy_analyse_report_view_controller.dart';
+
+/// 亲密度分析-分析报告Tab页
+class IntimacyAnalyseReportView
+    extends BaseView<IntimacyAnalyseReportController> {
+  const IntimacyAnalyseReportView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        Column(
+          children: [
+            SizedBox(height: 35.h),
+            // 亲密度档案
+            Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [_buildIntimacyArchives()],
+            ),
+            SizedBox(height: 12.h),
+            // 切换亲密度
+            _buildIntimacyArchivesSwitchLayout(),
+            SizedBox(height: 18.h),
+            // 报告预览
+            Expanded(child: _buildReportPreview()),
+          ],
+        ),
+        // 解锁按钮
+        Positioned.fill(
+          child: Align(
+            alignment: Alignment.bottomCenter,
+            child: _buildBottomLayout(),
+          ),
+        ),
+      ],
+    );
+  }
+
+  /// 亲密档案
+  Widget _buildIntimacyArchives() {
+    return SizedBox(
+      height: 100.h,
+      width: 200.w,
+      child: Stack(
+        // 默认内容都居中
+        alignment: Alignment.center,
+        children: [
+          // 用户1
+          Positioned(
+            left: 0,
+            child: _buildAvatar(
+              "",
+              defaultImage: Assets.images.iconDefaultAvatarMale.image().image,
+            ),
+          ),
+          // 用户2
+          Positioned(
+            left: 92.w,
+            top: 0,
+            child: _buildAvatar(
+              "",
+              defaultImage: Assets.images.iconDefaultAvatarFemale.image().image,
+            ),
+          ),
+          // 爱心
+          Positioned(
+            child: Assets.images.iconIntimacyAnalyseLove.image(
+              width: 42.w,
+              height: 42.2.w,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 亲密档案切换布局
+  Widget _buildIntimacyArchivesSwitchLayout() {
+    return GestureDetector(
+      onTap: () {
+        controller.clickIntimacyArchivesSwitch();
+      },
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Text(
+            "自己&小瑞",
+            style: TextStyle(
+              color: ColorName.black80,
+              fontSize: 13.sp,
+              fontWeight: FontWeight.w500,
+            ),
+          ),
+          SizedBox(width: 2.5.w),
+          Assets.images.iconIntimacyAnalyseArrow.image(width: 15, height: 15),
+        ],
+      ),
+    );
+  }
+
+  /// 报告预览布局-顶部布局-包含:图标和标题
+  Widget _buildReportPreviewTopLayout() {
+    return Row(
+      children: [
+        Assets.images.iconIntimacyAnalyseReportPreviewLove.image(
+          width: 50,
+          height: 48,
+        ),
+        Assets.images.iconIntimacyAnalyseReportPreviewTitle.image(
+          width: 178,
+          height: 24,
+        ),
+      ],
+    );
+  }
+
+  /// 报告预览
+  Widget _buildReportPreview() {
+    return Obx(() {
+      // 卡片背景
+      return Stack(
+        // 让大小,撑满父组件
+        fit: StackFit.expand,
+        children: [
+          // 中间内容
+          Container(
+            margin: EdgeInsets.only(left: 12, right: 12, bottom: 0),
+            padding: EdgeInsets.only(bottom: 12),
+            decoration: BoxDecoration(
+              image: DecorationImage(
+                image: Assets.images.bgIntimacyAnalyseReportPreview.provider(),
+                fit: BoxFit.fill,
+              ),
+            ),
+            // 圆角背景
+            child: Stack(
+              children: [
+                // 图标和标题
+                _buildReportPreviewTopLayout(),
+                Container(
+                  margin: EdgeInsets.only(
+                    top: 51,
+                    left: 12,
+                    right: 12,
+                    bottom: 5,
+                  ),
+                  decoration: ShapeDecoration(
+                    color: ColorName.white,
+                    shape: RoundedRectangleBorder(
+                      borderRadius: BorderRadius.circular(20.r),
+                    ),
+                  ),
+                  child: Padding(
+                    padding: const EdgeInsets.only(
+                      left: 14,
+                      right: 14,
+                      top: 20,
+                      bottom: 14,
+                    ),
+                    // 内容
+                    child: Stack(
+                      // 撑满父组件
+                      fit: StackFit.expand,
+                      children: [
+                        SingleChildScrollView(
+                          child: _buildReportContent(
+                            controller.reportPreviewData.value,
+                          ),
+                        ),
+                      ],
+                    ),
+                  ),
+                ),
+              ],
+            ),
+          ),
+          // 底部阴影
+          Positioned(
+            left: 0,
+            right: 0,
+            bottom: 0,
+            child: Container(
+              child: Assets.images.bgIntimacyAnalyseReportPreviewMask.image(
+                height: 126,
+              ),
+            ),
+          ),
+        ],
+      );
+    });
+  }
+
+  /// 报告内容
+  Widget _buildReportContent(IntimacyAnalyseReport report) {
+    return Column(
+      children: [
+        // 报告列表
+        ...report.list.map((ele) {
+          return ReportItemWidget(item: ele);
+        }),
+      ],
+    );
+  }
+
+  /// 底部布局
+  Widget _buildBottomLayout() {
+    return Column(
+      // 高度包裹内容,否则会撑满父组件
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        _buildReportPreviewBubble(),
+        _buildUnlockBtn(),
+        SizedBox(height: 20.h),
+      ],
+    );
+  }
+
+  /// 报告预览布局-气泡
+  Widget _buildReportPreviewBubble() {
+    return Container(
+      margin: EdgeInsets.only(left: 16, right: 58, bottom: 3),
+      padding: EdgeInsets.only(bottom: 3, right: 15),
+      decoration: BoxDecoration(
+        image: DecorationImage(
+          image: Assets.images.bgIntimacyAnalyseReportPreviewBubble.provider(),
+          fit: BoxFit.fill,
+        ),
+      ),
+      child: Stack(
+        children: [
+          // 人物图片
+          Assets.images.iconIntimacyAnalyseReportPreviewBubbleSymbol.image(
+            width: 47,
+            height: 28,
+          ),
+          Container(
+            padding: EdgeInsets.only(left: 38, top: 7, bottom: 8),
+            child: Text(
+              controller.reportPreviewBubbleText.value,
+              style: TextStyle(
+                color: ColorName.black65,
+                fontSize: 11.sp,
+                fontWeight: FontWeight.w400,
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 解锁按钮
+  Widget _buildUnlockBtn() {
+    return GestureDetector(
+      onTap: () {
+        controller.clickUnlockBtn();
+      },
+      child: Container(
+        margin: EdgeInsets.symmetric(horizontal: 16.w),
+        padding: EdgeInsets.symmetric(vertical: 14.h),
+        width: double.maxFinite,
+        decoration: ShapeDecoration(
+          color: ColorName.colorBrand,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(50.r),
+          ),
+        ),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Assets.images.iconIntimacyAnalyseUnlock.image(
+              width: 22,
+              height: 22,
+            ),
+            SizedBox(width: 4.w),
+            Text(
+              "解锁分析",
+              style: TextStyle(
+                color: ColorName.white,
+                fontSize: 16.sp,
+                fontWeight: FontWeight.w500,
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  /// 圆形头像
+  Widget _buildAvatar(String? imageUrl, {ImageProvider? defaultImage}) {
+    return CircleAvatarWidget(
+      imageUrl: imageUrl,
+      placeholderImage: defaultImage,
+      borderColor: ColorName.white,
+      borderWidth: 3.0,
+      backgroundColor: ColorName.white,
+      size: 96.0,
+    );
+  }
+}

+ 47 - 0
lib/module/intimacy_analyse/analyse_report/intimacy_analyse_report_view_controller.dart

@@ -0,0 +1,47 @@
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+
+import '../../../data/model/intimacy_analyse_report.dart';
+
+/// 亲密度分析-分析报告Tab-Controller
+@injectable
+class IntimacyAnalyseReportController extends BaseController {
+  /// 报告预览数据
+  Rx<IntimacyAnalyseReport> reportPreviewData = IntimacyAnalyseReport(list: [
+    AnalyseItem(
+      title: "性格匹配度",
+      sections: [
+        "互补型:一方外向活泼,另一方沉稳内敛,形成动态平衡。",
+        "相似型:三观一致,兴趣重叠,减少摩擦但需警惕新鲜感流失。",
+        "关键结论:差异是火花的来源,但核心价值观需一致(如家庭观、金钱观)。"
+      ],
+    ),
+    AnalyseItem(
+      title: "沟通模式分析",
+      sections: [
+        "语言风格:幽默调侃型 vs 理性分析型 → 需找到共同表达方式。",
+        "冲突解决:回避型 vs 直面型 → 建议建立“冷静-沟通”机制。",
+        "情感需求:一方需要高频互动,另一方偏好独立空间 → 需协商平衡点。"
+      ],
+    ),
+    AnalyseItem(
+      title: "爱情语言测试",
+      sections: [
+        "根据盖瑞·查普曼的“五种爱之语”理论,分析双方的情感表达偏好:",
+        "你的主要爱语:肯定的言辞(如情话、鼓励)",
+      ],
+    ),
+  ]).obs;
+
+  /// 报告预览-气泡文本
+  RxString reportPreviewBubbleText = '报告还有2565字未显示,点击按钮查看完整报告'.obs;
+
+  /// 切换亲密档案
+  void clickIntimacyArchivesSwitch() {
+  }
+
+  /// 解锁
+  void clickUnlockBtn() {
+  }
+}

+ 36 - 0
lib/module/intimacy_analyse/analyse_report/widget/report_item_widget.dart

@@ -0,0 +1,36 @@
+import 'package:flutter/cupertino.dart';
+import 'package:keyboard/module/intimacy_analyse/analyse_report/widget/report_section_widget.dart';
+import 'package:keyboard/module/intimacy_analyse/analyse_report/widget/report_title_widget.dart';
+
+import '../../../../data/model/intimacy_analyse_report.dart';
+
+/// 亲密度分析-报告列表项
+class ReportItemWidget extends StatelessWidget {
+  final AnalyseItem item;
+
+  const ReportItemWidget({required this.item, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      // 左对齐
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        SizedBox(height: 5,),
+        // 标题
+        ReportTitleWidget(title: item.title),
+        // 标题与段落的间隔
+        SizedBox(height: 10,),
+        ...item.sections.map<Widget>((ele) {
+          return Column(children: [
+            // 段落
+            ReportSectionWidget(selection: ele),
+            // 段落间隔
+            SizedBox(height: 2,)
+          ],);
+        }),
+      ],
+    );
+  }
+}

+ 43 - 0
lib/module/intimacy_analyse/analyse_report/widget/report_section_widget.dart

@@ -0,0 +1,43 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+
+import '../../../../resource/colors.gen.dart';
+
+/// 报告段落
+class ReportSectionWidget extends StatelessWidget {
+  final String selection;
+
+  const ReportSectionWidget({required this.selection, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        // 圆点
+        Container(
+          width: 3.w,
+          height: 3.w,
+          margin: EdgeInsets.symmetric(vertical: 9, horizontal: 8),
+          decoration: BoxDecoration(
+            color: ColorName.black72,
+            shape: BoxShape.circle,
+          ),
+        ),
+        // 段落
+        // Flexible,自动换行
+        Flexible(
+          child: Text(
+            selection,
+            style: TextStyle(
+              fontSize: 14.sp,
+              fontWeight: FontWeight.w500,
+              color: ColorName.black72,
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+}

+ 22 - 0
lib/module/intimacy_analyse/analyse_report/widget/report_title_widget.dart

@@ -0,0 +1,22 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/resource/colors.gen.dart';
+
+/// 亲密度报告标题
+class ReportTitleWidget extends StatelessWidget {
+  final String title;
+
+  const ReportTitleWidget({required this.title, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      title,
+      style: TextStyle(
+        fontSize: 16.sp,
+        fontWeight: FontWeight.w700,
+        color: ColorName.black80,
+      ),
+    );
+  }
+}

+ 12 - 6
lib/module/intimacy_analyse/intimacy_analyse_page.dart

@@ -2,12 +2,12 @@ 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/string.gen.dart';
-
+import 'package:keyboard/module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_view.dart';
 import '../../resource/assets.gen.dart';
 import '../../resource/colors.gen.dart';
 import '../../router/app_pages.dart';
 import '../../widget/tabbar/custom_tab_indicator.dart';
+import 'analyse_report/intimacy_analyse_report_view.dart';
 import 'intimacy_analyse_controller.dart';
 
 /// 亲密度报告页
@@ -50,6 +50,7 @@ class IntimacyAnalysePage extends BasePage<IntimacyAnalyseController> {
   /// 导航栏占位
   Widget _buildStatusBar() {
     return Container(
+      // 导航栏高度
       height: MediaQuery.of(Get.context!).padding.top,
       color: backgroundColor(),
     );
@@ -58,10 +59,13 @@ class IntimacyAnalysePage extends BasePage<IntimacyAnalyseController> {
   /// 顶部栏
   Widget _buildTopBar() {
     return Container(
-      height: kToolbarHeight,
+      // 宽度撑满父组件
       width: double.infinity,
-      padding: EdgeInsets.symmetric(horizontal: 16.0),
+      // 背景颜色
+      color: Colors.transparent,
+      // padding: EdgeInsets.symmetric(horizontal: 16.0),
       child: ConstrainedBox(
+        // 设置宽度为无限大,撑满父组件,否则Stack获取不到高度,会报错
         constraints: BoxConstraints(minWidth: double.infinity),
         child: Stack(
           alignment: Alignment.center,
@@ -148,8 +152,10 @@ class IntimacyAnalysePage extends BasePage<IntimacyAnalyseController> {
           controller.handlePageChange(index);
         },
         children: [
-          Center(child: Text(StringName.intimacyAnalyseTabReport)),
-          Center(child: Text(StringName.intimacyAnalyseTabScreenshotReply)),
+          // 报告Tab
+          IntimacyAnalyseReportView(),
+          // 截图回复Tab
+          IntimacyAnalyseScreenshotReplyView(),
         ],
       ),
     );

+ 7 - 0
lib/module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_controller.dart

@@ -0,0 +1,7 @@
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+
+/// 亲密度分析-截图回复Tab页-controller
+@injectable
+class IntimacyAnalyseScreenshotReplyController extends BaseController {
+}

+ 21 - 0
lib/module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_view.dart

@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:keyboard/base/base_view.dart';
+
+import '../../../resource/string.gen.dart';
+import 'intimacy_analyse_screenshot_reply_controller.dart';
+
+/// 亲密度分析-截图回复Tab页
+class IntimacyAnalyseScreenshotReplyView
+    extends BaseView<IntimacyAnalyseScreenshotReplyController> {
+  const IntimacyAnalyseScreenshotReplyView({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.transparent;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Center(child: Text(StringName.intimacyAnalyseTabScreenshotReply));
+  }
+}

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

@@ -105,6 +105,21 @@ class $AssetsImagesGen {
   AssetGenImage get bgIntimacyAnalyse =>
       const AssetGenImage('assets/images/bg_intimacy_analyse.webp');
 
+  /// File path: assets/images/bg_intimacy_analyse_report_preview.webp
+  AssetGenImage get bgIntimacyAnalyseReportPreview => const AssetGenImage(
+    'assets/images/bg_intimacy_analyse_report_preview.webp',
+  );
+
+  /// File path: assets/images/bg_intimacy_analyse_report_preview_bubble.webp
+  AssetGenImage get bgIntimacyAnalyseReportPreviewBubble => const AssetGenImage(
+    'assets/images/bg_intimacy_analyse_report_preview_bubble.webp',
+  );
+
+  /// File path: assets/images/bg_intimacy_analyse_report_preview_mask.webp
+  AssetGenImage get bgIntimacyAnalyseReportPreviewMask => const AssetGenImage(
+    'assets/images/bg_intimacy_analyse_report_preview_mask.webp',
+  );
+
   /// File path: assets/images/bg_keyboard.webp
   AssetGenImage get bgKeyboard =>
       const AssetGenImage('assets/images/bg_keyboard.webp');
@@ -304,6 +319,14 @@ class $AssetsImagesGen {
   AssetGenImage get iconDefaultAvatar =>
       const AssetGenImage('assets/images/icon_default_avatar.webp');
 
+  /// File path: assets/images/icon_default_avatar_female.webp
+  AssetGenImage get iconDefaultAvatarFemale =>
+      const AssetGenImage('assets/images/icon_default_avatar_female.webp');
+
+  /// File path: assets/images/icon_default_avatar_male.webp
+  AssetGenImage get iconDefaultAvatarMale =>
+      const AssetGenImage('assets/images/icon_default_avatar_male.webp');
+
   /// File path: assets/images/icon_dialog_close_black.webp
   AssetGenImage get iconDialogCloseBlack =>
       const AssetGenImage('assets/images/icon_dialog_close_black.webp');
@@ -356,6 +379,35 @@ class $AssetsImagesGen {
   AssetGenImage get iconGoodsInfoTitle =>
       const AssetGenImage('assets/images/icon_goods_info_title.webp');
 
+  /// File path: assets/images/icon_intimacy_analyse_arrow.webp
+  AssetGenImage get iconIntimacyAnalyseArrow =>
+      const AssetGenImage('assets/images/icon_intimacy_analyse_arrow.webp');
+
+  /// File path: assets/images/icon_intimacy_analyse_love.webp
+  AssetGenImage get iconIntimacyAnalyseLove =>
+      const AssetGenImage('assets/images/icon_intimacy_analyse_love.webp');
+
+  /// File path: assets/images/icon_intimacy_analyse_report_preview_bubble_symbol.webp
+  AssetGenImage get iconIntimacyAnalyseReportPreviewBubbleSymbol =>
+      const AssetGenImage(
+        'assets/images/icon_intimacy_analyse_report_preview_bubble_symbol.webp',
+      );
+
+  /// File path: assets/images/icon_intimacy_analyse_report_preview_love.webp
+  AssetGenImage get iconIntimacyAnalyseReportPreviewLove => const AssetGenImage(
+    'assets/images/icon_intimacy_analyse_report_preview_love.webp',
+  );
+
+  /// File path: assets/images/icon_intimacy_analyse_report_preview_title.webp
+  AssetGenImage get iconIntimacyAnalyseReportPreviewTitle =>
+      const AssetGenImage(
+        'assets/images/icon_intimacy_analyse_report_preview_title.webp',
+      );
+
+  /// File path: assets/images/icon_intimacy_analyse_unlock.webp
+  AssetGenImage get iconIntimacyAnalyseUnlock =>
+      const AssetGenImage('assets/images/icon_intimacy_analyse_unlock.webp');
+
   /// File path: assets/images/icon_keyboard_default_avatar.webp
   AssetGenImage get iconKeyboardDefaultAvatar =>
       const AssetGenImage('assets/images/icon_keyboard_default_avatar.webp');
@@ -623,6 +675,9 @@ class $AssetsImagesGen {
     bgDiscountTitle,
     bgGoApp,
     bgIntimacyAnalyse,
+    bgIntimacyAnalyseReportPreview,
+    bgIntimacyAnalyseReportPreviewBubble,
+    bgIntimacyAnalyseReportPreviewMask,
     bgKeyboard,
     bgKeyboardLove,
     bgKeyboardManage,
@@ -671,6 +726,8 @@ class $AssetsImagesGen {
     iconCustomCharacterAddMarket,
     iconCustomDialogClose,
     iconDefaultAvatar,
+    iconDefaultAvatarFemale,
+    iconDefaultAvatarMale,
     iconDialogCloseBlack,
     iconDialogPayFail,
     iconDialogPayFailService,
@@ -684,6 +741,12 @@ class $AssetsImagesGen {
     iconDiscountTitle,
     iconDiscountTutorial,
     iconGoodsInfoTitle,
+    iconIntimacyAnalyseArrow,
+    iconIntimacyAnalyseLove,
+    iconIntimacyAnalyseReportPreviewBubbleSymbol,
+    iconIntimacyAnalyseReportPreviewLove,
+    iconIntimacyAnalyseReportPreviewTitle,
+    iconIntimacyAnalyseUnlock,
     iconKeyboardDefaultAvatar,
     iconKeyboardLoveIndex,
     iconKeyboardLoveLogo,

+ 3 - 0
lib/resource/colors.gen.dart

@@ -61,6 +61,9 @@ class ColorName {
   /// Color: #B3000000
   static const Color black70 = Color(0xB3000000);
 
+  /// Color: #B8000000
+  static const Color black72 = Color(0xB8000000);
+
   /// Color: #B000000F
   static const Color black75 = Color(0xB000000F);
 

+ 4 - 0
lib/router/app_pages.dart

@@ -26,6 +26,8 @@ import '../module/character_custom/character_custom_page.dart';
 import '../module/character_custom/detail/character_custom_detail_controller.dart';
 import '../module/character_custom/list/character_custom_list_page.dart';
 import '../module/feedback/feedback_page.dart';
+import '../module/intimacy_analyse/analyse_report/intimacy_analyse_report_view_controller.dart';
+import '../module/intimacy_analyse/screenshot_reply/intimacy_analyse_screenshot_reply_controller.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';
@@ -86,6 +88,8 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<ProfileEditController>());
     lazyPut(() => getIt.get<KeyboardGuidePageController>());
     lazyPut(() => getIt.get<IntimacyAnalyseController>());
+    lazyPut(() => getIt.get<IntimacyAnalyseReportController>());
+    lazyPut(() => getIt.get<IntimacyAnalyseScreenshotReplyController>());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {

+ 121 - 0
lib/widget/avatar/avatar_image_widget.dart

@@ -0,0 +1,121 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/material.dart';
+
+/// 圆形头像组件
+class CircleAvatarWidget extends StatelessWidget {
+  /// 本地图片
+  final ImageProvider? image;
+
+  /// 网络图片URL
+  final String? imageUrl;
+
+  /// 加载失败时的占位图
+  final ImageProvider? placeholderImage;
+
+  /// 头像大小
+  final double size;
+
+  /// 边框颜色
+  final Color borderColor;
+
+  /// 边框宽度
+  final double borderWidth;
+
+  /// 背景颜色
+  final Color backgroundColor;
+
+  /// 加载时的渐变时长
+  final Duration fadeInDuration;
+
+  /// 加载中的占位
+  final PlaceholderWidgetBuilder? placeholder;
+
+  const CircleAvatarWidget({
+    super.key,
+    this.image,
+    this.imageUrl,
+    this.placeholderImage,
+    this.size = 40.0,
+    this.borderColor = Colors.white,
+    this.borderWidth = 2.0,
+    this.backgroundColor = const Color(0xFFEEEEEE),
+    this.fadeInDuration = const Duration(milliseconds: 500),
+    this.placeholder,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: size,
+      height: size,
+      decoration: BoxDecoration(
+        shape: BoxShape.circle,
+        border: Border.all(color: borderColor, width: borderWidth),
+      ),
+      // 裁切圆形
+      child: ClipOval(child: _buildContent()),
+    );
+  }
+
+  /// 构建头像内容
+  Widget _buildContent() {
+    // 优先使用网络图片
+    if (imageUrl != null && imageUrl!.isNotEmpty) {
+      return CachedNetworkImage(
+        imageUrl: imageUrl!,
+        width: size,
+        height: size,
+        fit: BoxFit.cover,
+        fadeInDuration: fadeInDuration,
+        placeholder: placeholder ?? _buildLoadingPlaceholder,
+        errorWidget: (_, __, ___) => _buildPlaceholder(), // 错误时显示占位
+      );
+    }
+    // 没有,再使用本地图片
+    else if (image != null) {
+      return Image(
+        image: image!,
+        width: size,
+        height: size,
+        fit: BoxFit.cover,
+        errorBuilder: (_, __, ___) => _buildPlaceholder(),
+      );
+    }
+    // 无图片状态
+    else {
+      return _buildPlaceholder();
+    }
+  }
+
+  /// 加载中占位
+  Widget _buildLoadingPlaceholder(BuildContext context, String url) {
+    return Container(
+      color: Colors.grey[200],
+      alignment: Alignment.center,
+      child: const CircularProgressIndicator(strokeWidth: 2),
+    );
+  }
+
+  /// 错误/默认的占位组件
+  Widget _buildPlaceholder() {
+    // 优先使用自定义占位图
+    if (placeholderImage != null) {
+      return Image(
+        image: placeholderImage!,
+        width: size,
+        height: size,
+        fit: BoxFit.cover,
+        // 占位图加载失败时,显示一个纯色背景
+        errorBuilder: (_, __, ___) => _buildDefaultPlaceholder(),
+      );
+    }
+
+    // 默认占位
+    return _buildDefaultPlaceholder();
+  }
+
+  /// 占位图加载失败时,显示一个纯色背景
+  Widget _buildDefaultPlaceholder() {
+    return Container(color: backgroundColor, width: size, height: size);
+  }
+}