From dc8f8f1d0b7796b9d6a7e94e303a660150eabc36 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Sat, 2 Aug 2025 17:59:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9ai=E5=8A=A9=E6=89=8B=E5=BC=95?= =?UTF-8?q?=E5=AF=BC=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/widgets/intro_overlay_builder.dart | 71 ++++ lib/core/widgets/select_location_map.dart | 15 - lib/data/services/novice_guide_service.dart | 43 ++- lib/modules/agent/views/agent_view.dart | 343 ++++++++++++-------- 4 files changed, 325 insertions(+), 147 deletions(-) create mode 100644 lib/core/widgets/intro_overlay_builder.dart delete mode 100644 lib/core/widgets/select_location_map.dart diff --git a/lib/core/widgets/intro_overlay_builder.dart b/lib/core/widgets/intro_overlay_builder.dart new file mode 100644 index 000000000..5df0ba579 --- /dev/null +++ b/lib/core/widgets/intro_overlay_builder.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_intro/flutter_intro.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class IntroOverlayBuilder { + static Widget aiBuilder( + StepWidgetParams params, + String text, { + bool alignment = true, + Function? onNext, + Function? onFinish, + }) { + return Column( + crossAxisAlignment: + alignment ? CrossAxisAlignment.start : CrossAxisAlignment.end, + children: [ + Padding( + padding: EdgeInsets.only(bottom: 10.w), + child: Text( + text, + style: TextStyle( + fontSize: 15.sp, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + Row( + children: [ + if (onNext != null) + _button( + '确定输入', + () { + params.onNext!(); + onNext(); + }, + ), + if (onFinish != null) + _button( + '下一步', + () { + params.onFinish(); + onFinish(); + }, + ), + ], + ), + ], + ); + } + + static Widget _button(String text, Function() onTap) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.r), + border: Border.all(color: Colors.white), + ), + child: Text( + text, + style: TextStyle( + fontSize: 14.sp, + color: Colors.white, + ), + ), + ), + ); + } +} diff --git a/lib/core/widgets/select_location_map.dart b/lib/core/widgets/select_location_map.dart deleted file mode 100644 index c783901ed..000000000 --- a/lib/core/widgets/select_location_map.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class SelectLocationMap extends StatefulWidget { - const SelectLocationMap({super.key}); - - @override - State createState() => _SelectLocationMapState(); -} - -class _SelectLocationMapState extends State { - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart index aeb3baa39..b875236d8 100644 --- a/lib/data/services/novice_guide_service.dart +++ b/lib/data/services/novice_guide_service.dart @@ -6,7 +6,48 @@ class NoviceGuideService extends GetxService { final GetStorage _storage = GetStorage(); bool isHomeGuide = true; - bool isAIGuide = true; + bool isAIGuide = false; + + int aiStep = 0; + + List aiStepsList = [ + { + 'start': '对小言说:“今天天气怎么样”', + 'input': '今天天气怎么样', + 'send': '小言会为你实时为你播报天气', + 'end': '小言能实时为你播报天气,试试其他指令吧', + }, + { + 'start': '对小言说:“请播放一组摇滚乐”', + 'input': '请播放一组摇滚乐', + 'send': '小言会为你搜寻到一组音乐', + 'end': '小言已成功为您搜寻到一组音乐,试试其他指令吧', + }, + { + 'start': '对小言说:“导航到深圳机场”', + 'input': '导航到深圳机场', + 'send': '小言会为你规划路线', + 'end': '小言已成功为您导航到深圳机场,试试其他指令吧', + }, + { + 'start': '对小言说:“明天下午3点创建会议提醒”', + 'input': '明天下午3点创建会议提醒', + 'send': '小言会为你创建会议提醒', + 'end': '小言已成功为您创建会议提醒,试试其他指令吧', + }, + { + 'start': '对小言说:“拨打妈妈的电话”', + 'input': '拨打妈妈的电话', + 'send': '小言会为你拨打电话', + 'end': '小言已成功为您拨打电话,试试其他指令吧', + }, + { + 'start': '对小言说:“给爸爸发短信说晚上回家吃饭’”', + 'input': '给爸爸发短信说晚上回家吃饭’', + 'send': '小言会为你发送短信', + 'end': '短信已发送,语音操控更便捷', + }, + ]; @override void onInit() { diff --git a/lib/modules/agent/views/agent_view.dart b/lib/modules/agent/views/agent_view.dart index d989cd037..3a89e0f01 100644 --- a/lib/modules/agent/views/agent_view.dart +++ b/lib/modules/agent/views/agent_view.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_intro/flutter_intro.dart'; import 'package:get/get.dart'; +import '../../../core/widgets/intro_overlay_builder.dart'; import '../controllers/agent_controller.dart'; import '../controllers/message_action_controller.dart'; import 'message_bubble.dart'; @@ -197,47 +198,79 @@ class _AgentViewState extends State with WidgetsBindingObserver { children: [ // 聊天内容 Expanded( - child: Obx( - () => Align( - alignment: Alignment.topCenter, - child: ListView.builder( - controller: controller.scrollController, - shrinkWrap: true, - reverse: true, - padding: EdgeInsets.symmetric( - vertical: 16.h, horizontal: 16.w), - itemCount: controller.messages.length + - (controller.hasMoreMessages.value ? 1 : 0), - itemBuilder: (context, index) { - // 如果是最后一项且有更多消息,显示加载指示器 - if (index == controller.messages.length && - controller.hasMoreMessages.value) { - return _buildLoadMoreIndicator(); - } - - final message = controller - .messages[controller.messages.length - 1 - index]; - return Padding( - padding: EdgeInsets.only(bottom: 8.h), - child: GestureDetector( - onLongPressStart: - (LongPressStartDetails details) { - messageActionController.onLongPressMessage( - context, isDarkMode, details, message); - }, - child: MessageBubble( - isUser: message.isUser, - message: message.text, - timestamp: message.timestamp, - isRecognizing: message.isRecognizing, - hasImage: message.hasImage, - imagePath: message.imagePath, - meta: message.meta, - ), - )); + child: Stack( + children: [ + IntroStepBuilder( + group: 'ai', + order: 3, + overlayBuilder: (params) { + return IntroOverlayBuilder.aiBuilder( + params, + controller.noviceGuideService.aiStepsList[ + controller.noviceGuideService.aiStep]['end'], + onFinish: () async { + if (controller.noviceGuideService.aiStep < 5) { + await Future.delayed( + const Duration(milliseconds: 300)); + controller.noviceGuideService.aiStep++; + Intro.of(Get.context!) + .start(group: 'ai', reset: true); + } + }, + ); }, + builder: (BuildContext context, GlobalKey key) => Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + key: key, + width: double.infinity, + height: 430.w, + ), + ), ), - ), + Obx( + () => Align( + alignment: Alignment.topCenter, + child: ListView.builder( + controller: controller.scrollController, + shrinkWrap: true, + reverse: true, + padding: EdgeInsets.symmetric( + vertical: 16.h, horizontal: 16.w), + itemCount: controller.messages.length + + (controller.hasMoreMessages.value ? 1 : 0), + itemBuilder: (context, index) { + // 如果是最后一项且有更多消息,显示加载指示器 + if (index == controller.messages.length && + controller.hasMoreMessages.value) { + return _buildLoadMoreIndicator(); + } + + final message = controller.messages[ + controller.messages.length - 1 - index]; + return Padding( + padding: EdgeInsets.only(bottom: 8.h), + child: GestureDetector( + onLongPressStart: + (LongPressStartDetails details) { + messageActionController.onLongPressMessage( + context, isDarkMode, details, message); + }, + child: MessageBubble( + isUser: message.isUser, + message: message.text, + timestamp: message.timestamp, + isRecognizing: message.isRecognizing, + hasImage: message.hasImage, + imagePath: message.imagePath, + meta: message.meta, + ), + )); + }, + ), + ), + ), + ], ), ), @@ -392,7 +425,23 @@ class _AgentViewState extends State with WidgetsBindingObserver { return IntroStepBuilder( group: 'ai', order: 1, - text: '对小言说“今天天气怎么样”', + overlayBuilder: (params) { + return IntroOverlayBuilder.aiBuilder( + params, + controller.noviceGuideService + .aiStepsList[ + controller.noviceGuideService + .aiStep]['start'], + onNext: () { + controller.textController.text = + controller.noviceGuideService + .aiStepsList[ + controller + .noviceGuideService + .aiStep]['input']; + }, + ); + }, onWidgetLoad: () { controller.noviceGuideService .startAIGuide(); @@ -630,111 +679,143 @@ class _AgentViewState extends State with WidgetsBindingObserver { SizedBox(width: 8.w), // 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮 - Obx(() { - final hasText = controller.hasTextInput.value; - final hasImage = - controller.isImageInputActive.value; - final shouldShowSend = hasText || hasImage; - final shouldShowExpand = - controller.shouldShowExpandButton.value; + Builder(builder: (context) { + StepWidgetParams? widgetParams; + return IntroStepBuilder( + group: 'ai', + order: 2, + onHighlightWidgetTap: () { + widgetParams!.onNext!(); + controller.sendMessage(); + }, + overlayBuilder: (params) { + widgetParams = params; + return IntroOverlayBuilder.aiBuilder( + params, + '点击发送,${controller.noviceGuideService.aiStepsList[controller.noviceGuideService.aiStep]['send']}', + alignment: false, + ); + }, + builder: + (BuildContext context, GlobalKey key) => + Obx( + key: key, + () { + final hasText = + controller.hasTextInput.value; + final hasImage = + controller.isImageInputActive.value; + final shouldShowSend = + hasText || hasImage; + final shouldShowExpand = controller + .shouldShowExpandButton.value; - if (shouldShowSend) { - // 显示发送按钮(可能还有展开按钮) - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 展开按钮(在发送按钮上方) - if (shouldShowExpand && hasText) - Padding( - padding: - EdgeInsets.only(bottom: 40.h), - child: GestureDetector( + if (shouldShowSend) { + // 显示发送按钮(可能还有展开按钮) + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 展开按钮(在发送按钮上方) + if (shouldShowExpand && hasText) + Padding( + padding: EdgeInsets.only( + bottom: 40.h), + child: GestureDetector( + onTap: () { + // 打开底部弹出编辑模式 + controller + .openFullScreenEdit(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: + Colors.transparent, + builder: (context) => + const FullScreenEditor(), + ).then((_) { + // 弹出页关闭时的回调 + controller + .closeFullScreenEdit(); + }); + }, + child: Container( + width: 32.w, + height: 32.w, + decoration: BoxDecoration( + color: isDarkMode + ? Colors.grey[700] + : Colors.grey[300], + borderRadius: + BorderRadius.circular( + 16.w), + ), + child: Icon( + Icons.expand_less, + color: isDarkMode + ? Colors.grey[300] + : Colors.grey[700], + size: 20.sp, + ), + ), + ), + ), + // 发送按钮 + GestureDetector( + onTap: () => + controller.sendMessage(), + child: Container( + width: 44.w, + height: 44.w, + decoration: BoxDecoration( + color: primaryColor, + borderRadius: + BorderRadius.circular( + 22.w), + ), + child: Icon( + Icons.send, + color: Colors.white, + size: 22.sp, + ), + ), + ), + ], + ); + } else { + // 显示'+'选择按钮 + return Obx(() { + final isExpanded = controller + .isPlusOptionsExpanded.value; + return GestureDetector( onTap: () { - // 打开底部弹出编辑模式 - controller.openFullScreenEdit(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: - Colors.transparent, - builder: (context) => - const FullScreenEditor(), - ).then((_) { - // 弹出页关闭时的回调 - controller - .closeFullScreenEdit(); - }); + controller.togglePlusOptions(); }, child: Container( - width: 32.w, - height: 32.w, + width: 44.w, + height: 44.w, decoration: BoxDecoration( color: isDarkMode - ? Colors.grey[700] - : Colors.grey[300], + ? Colors.grey[800] + : Colors.grey[200], borderRadius: - BorderRadius.circular(16.w), + BorderRadius.circular(22.w), ), child: Icon( - Icons.expand_less, + isExpanded + ? Icons.close + : Icons.add, color: isDarkMode ? Colors.grey[300] : Colors.grey[700], - size: 20.sp, + size: 22.sp, ), ), - ), - ), - // 发送按钮 - GestureDetector( - onTap: () => controller.sendMessage(), - child: Container( - width: 44.w, - height: 44.w, - decoration: BoxDecoration( - color: primaryColor, - borderRadius: - BorderRadius.circular(22.w), - ), - child: Icon( - Icons.send, - color: Colors.white, - size: 22.sp, - ), - ), - ), - ], - ); - } else { - // 显示'+'选择按钮 - return Obx(() { - final isExpanded = - controller.isPlusOptionsExpanded.value; - return GestureDetector( - onTap: () { - controller.togglePlusOptions(); - }, - child: Container( - width: 44.w, - height: 44.w, - decoration: BoxDecoration( - color: isDarkMode - ? Colors.grey[800] - : Colors.grey[200], - borderRadius: - BorderRadius.circular(22.w), - ), - child: Icon( - isExpanded ? Icons.close : Icons.add, - color: isDarkMode - ? Colors.grey[300] - : Colors.grey[700], - size: 22.sp, - ), - ), - ); - }); - } + ); + }); + } + }, + ), + ); }), ], ),