| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- import 'package:flutter/material.dart';
- import 'package:flutter_screenutil/flutter_screenutil.dart';
- import 'package:get/get.dart';
- import 'package:keyboard/module/keyboard_guide/keyboard_guide_controller.dart';
- import 'package:keyboard/router/app_pages.dart';
- import 'package:keyboard/utils/default_keyboard_helper.dart';
- import 'package:keyboard/utils/keyboard_guide_record_util.dart' show KeyboardGuideRecordUtil;
- import 'package:keyboard/utils/method_chanel_ios_util.dart';
- import 'package:keyboard/utils/toast_util.dart';
- import 'package:keyboard/widget/platform_util.dart';
- import 'package:lottie/lottie.dart';
- import '../../base/base_page.dart';
- import '../../data/bean/keyboard_guide_msg.dart';
- import '../../data/consts/event_report.dart';
- import '../../handler/event_handler.dart';
- import '../../resource/assets.gen.dart';
- import '../../resource/colors.gen.dart';
- import '../../resource/string.gen.dart';
- import '../../utils/clipboard_util.dart';
- import '../../utils/url_launcher_util.dart';
- import '../../widget/ai/ai_generate_tip_widget.dart';
- import '../../widget/app_lifecycle_widget.dart';
- import '../intimacy_scale/intimacy_scale_page.dart';
- import 'enums/keyboard_guide_msg_type.dart';
- /// 键盘引导页面
- class KeyboardGuidePage extends BasePage<KeyboardGuidePageController> {
- const KeyboardGuidePage({super.key});
- /// 跳转到键盘引导页
- static void start() {
- Get.toNamed(RoutePath.keyboardGuide);
- }
- /// 跳转并关闭当前页
- static void startAndOffMe() {
- Get.offNamed(RoutePath.keyboardGuide);
- }
- @override
- immersive() {
- return false;
- }
- @override
- Widget buildBody(BuildContext context) {
- return Scaffold(
- backgroundColor: backgroundColor(),
- body: AppLifecycleWidget(
- onAppLifecycleCallback: (isForeground) {
- // 完成教程
- controller.setNotFirstShowKeyboardTutorial();
- if (isForeground) {
- // 切换到前台时,重新检查设置,更新按钮状态
- controller.checkSetting();
- // 如果选择为默认键盘了,则尝试显示引导弹窗
- if (controller.isDefaultKeyboard.value && KeyboardGuideRecordUtil.isFirstShowKeyboardGuide()) {
- controller.showGuideOverlayDialog();
- }
- }
- },
- child: Column(
- children: [
- // 使用 Obx 监听 isDefaultKeyboard 的变化
- Obx(() {
- // 当 isDefaultKeyboard 变为 true 时,显示引导对话框
- if (controller.isDefaultKeyboard.value) {
- // 使用 Future.microtask 确保在构建完成后调用
- Future.microtask(() => controller.showGuideOverlayDialog());
- }
- // 返回一个空的 SizedBox,不影响 UI
- return SizedBox.shrink();
- }),
- // 标题栏
- _buildTitleBar(),
- // 消息列表
- Expanded(flex: 1, child: _buildContent()),
- // 底部输入栏
- _buildBottomInput(),
- ],
- ),
- ),
- );
- }
- // 标题栏
- Widget _buildTitleBar() {
- return Container(
- color: backgroundColor(),
- height: kToolbarHeight,
- padding: EdgeInsets.symmetric(horizontal: 16.0),
- child: Row(
- children: [
- // 返回按钮
- GestureDetector(
- onTap: controller.clickBack,
- child: Assets.images.iconMineBackArrow.image(
- width: 24.w,
- height: 24.h,
- ),
- ),
- // 标题
- Expanded(
- child: Container(
- alignment: Alignment.center,
- child: Text("", style: const TextStyle(fontSize: 18)),
- ),
- ),
- // 右侧按钮
- GestureDetector(
- onTap: () async {
- bool result = await UrlLauncherUtil.openWeChat();
- EventHandler.report(EventId.event_03007);
- if (!result) {
- ToastUtil.show(StringName.keyboardGuideWechatNotInstall);
- }
- },
- child: Container(
- padding: EdgeInsets.only(
- left: 12.w,
- right: 14.w,
- top: 10.w,
- bottom: 10.w,
- ),
- decoration: BoxDecoration(
- image: DecorationImage(
- image: Assets.images.bgGoApp.provider(),
- fit: BoxFit.fill,
- ),
- ),
- child: Row(
- children: [
- Assets.images.iconWechat.image(height: 22.w, width: 22.w),
- SizedBox(width: 1.0),
- Text(
- StringName.keyboardGuideGoWechat,
- style: TextStyle(
- color: ColorName.black80,
- fontSize: 12,
- fontWeight: FontWeight.w400,
- ),
- ),
- ],
- ),
- ),
- ),
- ],
- ),
- );
- }
- /// 内容
- Widget _buildContent() {
- return Obx(() {
- // 选择了默认键盘,显示聊天列表
- if (controller.isDefaultKeyboard.value) {
- return _buildChatList();
- } else {
- // 未选择,显示引导动画
- return _buildGuideAnimation();
- }
- });
- }
- /// 引导动画
- Widget _buildGuideAnimation() {
- Widget animationWidget;
- if (PlatformUtil.isIOS) {
- animationWidget = Lottie.asset(
- Assets.anim.animKeyboardFloatingWindowChooseKeyboardIos,
- repeat: true,
- );
- } else if (PlatformUtil.isAndroid) {
- animationWidget = Lottie.asset(
- Assets.anim.animKeyboardFloatingWindowChooseKeyboardAndroid,
- repeat: true,
- );
- } else {
- animationWidget = SizedBox.shrink();
- }
- return Container(child: animationWidget);
- }
- /// 聊天列表
- Widget _buildChatList() {
- return Obx(() {
- return ListView.builder(
- controller: controller.scrollController,
- itemCount: controller.msgList.length + 1,
- itemBuilder: (BuildContext context, int index) {
- if (index == controller.msgList.length) {
- return (controller.msgList.length > 5)
- ? Container(
- child: _buildAiGenerateTip(),
- )
- : SizedBox();
- }
- KeyboardGuideMsg msg = controller.msgList[index];
- return _buildMsgItem(msg, index);
- },
- );
- });
- }
- Widget _buildAiGenerateTip() {
- return Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- AiGenerateTipWidget.normalTip(textColor: Colors.black.withAlpha(92)),
- ],
- );
- }
- /// 构建底部输入框
- Widget _buildBottomInput() {
- return Center(
- child: Column(
- children: [
- Container(
- color: ColorName.msgInputBar,
- padding: const EdgeInsets.symmetric(
- vertical: 11.0,
- horizontal: 12.0,
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Expanded(
- flex: 1,
- // 输入框的圆角边框
- child: Container(
- decoration: BoxDecoration(
- color: ColorName.white,
- borderRadius: BorderRadius.circular(10.0),
- ),
- child: Obx(() {
- return TextField(
- // 是否可用,选择了默认键盘时,才可用
- enabled:
- controller.isIOS.value
- ? true
- : controller.isDefaultKeyboard.value,
- // 是否自动获取焦点
- autofocus: false,
- style: TextStyle(
- color: ColorName.black80,
- fontSize: 14.0,
- fontWeight: FontWeight.w500,
- ),
- // 设置光标颜色
- cursorColor: ColorName.inputCursor,
- // 光标宽度
- cursorWidth: 2.0,
- // 光标圆角
- cursorRadius: Radius.circular(2),
- // 设置按钮显示为发送
- textInputAction: TextInputAction.send,
- // 用户点击软键盘的发送按钮时,触发回调
- onSubmitted: (value) {
- var msg = controller.editingController.text;
- controller.sendMsg(msg);
- // 保持输入框焦点获取,不降下键盘
- controller.requestInputFocus();
- },
- // 输入框焦点
- focusNode: controller.inputFocusNode,
- // 点击外部区域,关闭软键盘
- onTapUpOutside: (event) {
- // if (PlatformUtil.isIOS) {
- // controller.clearInputFocus();
- // }
- },
- // 输入框控制器
- controller: controller.editingController,
- decoration: InputDecoration(
- // 提示文字
- hintText: StringName.keyboardGuideInputHint,
- hintStyle: TextStyle(
- fontSize: 14.0,
- fontWeight: FontWeight.w400,
- color: ColorName.black40,
- ),
- // 去掉默认的边框
- border: InputBorder.none,
- // 设置输入框的内边距
- contentPadding: EdgeInsets.symmetric(
- horizontal: 9.0,
- vertical: 13.0,
- ),
- ),
- );
- }),
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- );
- }
- /// 构建聊天气泡
- Widget _buildMsgBubble(KeyboardGuideMsg msg) {
- // 设置气泡的外边距,让气泡不易过长
- double marginValue = 35.0;
- EdgeInsets marginEdgeInsets;
- if (msg.isMe) {
- marginEdgeInsets = EdgeInsets.only(left: marginValue);
- } else {
- marginEdgeInsets = EdgeInsets.only(right: marginValue);
- }
- // 圆角大小
- double radiusSize = 14.0;
- // 背景圆角
- BorderRadius bgBorderRadius;
- if (msg.isMe) {
- bgBorderRadius = BorderRadius.only(
- topLeft: Radius.circular(radiusSize),
- topRight: Radius.circular(0),
- bottomLeft: Radius.circular(radiusSize),
- bottomRight: Radius.circular(radiusSize),
- );
- } else {
- bgBorderRadius = BorderRadius.only(
- topLeft: Radius.circular(0),
- topRight: Radius.circular(radiusSize),
- bottomLeft: Radius.circular(radiusSize),
- bottomRight: Radius.circular(0),
- );
- }
- // Flexible,文本超过一行时,自动换行,并且不超过最大宽度,不超过一行时,则自动包裹内容
- return Flexible(
- child: Container(
- padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 10.0),
- margin: marginEdgeInsets,
- decoration: BoxDecoration(
- color: msg.isMe ? ColorName.msgBubbleMe : ColorName.msgBubbleTa,
- borderRadius: bgBorderRadius,
- ),
- child: GestureDetector(
- onTap: () {
- // 复制内容到剪切板
- if (msg.type == KeyboardGuideMsgType.copy.type) {
- ClipboardUtil.copyToClipboard(msg.content);
- ToastUtil.show(StringName.copySuccess);
- } else if (msg.type == KeyboardGuideMsgType.intimacySetting.type) {
- // 跳转到亲密度设置页
- IntimacyScalePage.start();
- }
- },
- child: Row(
- // 宽高包裹内容
- mainAxisSize: MainAxisSize.min,
- // 图标和文本,垂直居中
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Flexible(
- // 消息文本
- child: Text(
- msg.content,
- style: TextStyle(
- fontSize: 14.0,
- color: ColorName.black80,
- fontWeight: FontWeight.w500,
- height: 1.5,
- ),
- softWrap: true,
- ),
- ),
- // 只有对方发送的,才有操作按钮
- if (!msg.isMe)
- Visibility(
- visible: msg.type != KeyboardGuideMsgType.normal.type,
- child: Padding(
- padding: EdgeInsets.only(left: 8.0),
- child: _buildMsgActionBtn(msg),
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
- /// 消息操作按钮
- Widget _buildMsgActionBtn(KeyboardGuideMsg msg) {
- if (msg.type == KeyboardGuideMsgType.copy.type) {
- return Assets.images.iconCopy.image(width: 18.w, height: 18.w);
- } else if (msg.type == KeyboardGuideMsgType.intimacySetting.type) {
- return Assets.images.iconSetting.image(width: 18.w, height: 18.w);
- } else {
- return SizedBox.shrink();
- }
- }
- /// 构建聊天消息列表项
- Widget _buildMsgItem(KeyboardGuideMsg msg, int index) {
- return Obx(() {
- Widget content;
- // 自己发的
- if (msg.isMe) {
- content = Row(
- // 如果是自己发的,则在右边
- mainAxisAlignment: MainAxisAlignment.end,
- // 顶部对齐
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // 聊天气泡
- _buildMsgBubble(msg),
- SizedBox(width: 9.w),
- // 头像
- _buildAvatar(msg),
- ],
- );
- } else {
- // 对方发的
- content = Row(
- // 如果是自己发的,则在右边
- mainAxisAlignment: MainAxisAlignment.start,
- // 顶部对齐
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // 头像
- _buildAvatar(msg),
- SizedBox(width: 9.w),
- // 聊天气泡
- _buildMsgBubble(msg),
- ],
- );
- }
- bool isTargetGuildMsg = controller.guideMsgIndex.value == index;
- return Container(
- margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.0.h),
- child:
- isTargetGuildMsg
- ? Container(key: controller.guideMsgGlobalKey, child: content)
- : content,
- );
- });
- }
- /// 构建头像
- Widget _buildAvatar(KeyboardGuideMsg msg) {
- double avatarSize = 36.0;
- return CircleAvatar(
- radius: 20,
- child:
- msg.isMe
- ? Assets.images.iconDefaultAvatar.image(
- height: avatarSize,
- width: avatarSize,
- )
- : Assets.images.iconTaAvatar.image(
- height: avatarSize,
- width: avatarSize,
- ),
- );
- }
- }
|