import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:lottie/lottie.dart'; import '../../../../common/api/index.dart'; import '../../../../common/models/enums/index.dart'; import '../../../../common/models/im/index.dart'; import '../../../../common/routers/index.dart'; import '../../../../common/utils/loading.dart'; import 'index.dart'; class SpeechAssistantChatPage extends GetView { const SpeechAssistantChatPage({Key? key}) : super(key: key); static Future open() async { if (ProgramCenterService.to.speechScript().isEmpty) { Loading.showError('没有配置语音脚本,请联系管理员'); return ; } await Get.toNamed(O2OARoutes.homeImChatSpeechAssistant); } Widget _contentView(BuildContext context) { return Obx(() => controller.state.chatList.length > 0 ? _msgListView() : _initRandomCommandListView(context)); } /// 初始化随机命令列表 Widget _initRandomCommandListView(BuildContext context) { return Column( children: [ const SizedBox(height: 24), Padding( padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('im_chat_speech_assistant_tips'.tr, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center)), const SizedBox(height: 12), Expanded( flex: 1, child: Obx(() => ListView.builder( itemCount: controller.state.initCommandList.length, itemBuilder: ((context, index) { final item = controller.state.initCommandList[index]; return Padding( padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text( '“$item”', style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, )); })))) ], ); } /// 对话列表 Widget _msgListView() { return Padding( padding: const EdgeInsets.all(12), child: Obx(() => ListView.separated( padding: const EdgeInsets.only(bottom: 10), // reverse: true, // 反转 shrinkWrap: true, separatorBuilder: (context, index) { return const SizedBox(height: 1); }, itemCount: controller.state.chatList.length, itemBuilder: (context, index) { final item = controller.state.chatList[index]; var msg = item.msg ?? ''; if (msg.isEmpty) { msg = item.err ?? ''; } if (item.side == ImSpeechAssistantResponse.rightSide) { return _msgViewRight(msg); } else { return _msgViewLeft(msg); } }))); } Widget _msgViewLeft(String msg) { return Padding( padding: const EdgeInsets.only(right: 50.0, top: 5, bottom: 5), child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 30), Flexible( child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: const BorderRadius.only( topRight: Radius.circular(18), bottomLeft: Radius.circular(18), bottomRight: Radius.circular(18), ), ), child: _textView(msg, false), ), ) ])); } Widget _msgViewRight(String msg) { return Padding( padding: const EdgeInsets.only(left: 50, top: 5, bottom: 5), child: Row(mainAxisAlignment: MainAxisAlignment.end, children: [ const SizedBox(height: 30), Flexible( child: Container( padding: const EdgeInsets.all(14), decoration: const BoxDecoration( color: Color.fromARGB(255, 0, 127, 255), borderRadius: BorderRadius.only( topLeft: Radius.circular(18), bottomLeft: Radius.circular(18), bottomRight: Radius.circular(18), ), ), child: _textView(msg, true), ), ) ])); } Widget _textView(String text, bool isRight) { return Text( text, style: TextStyle(color: isRight ? Colors.white : Colors.black, fontSize: 14), ); } // 底部操作栏 Widget _bottomBarView(BuildContext context) { return Column( children: [ SizedBox(height: 5), Padding( padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20), child: GestureDetector( onTapDown: (x) => controller.startRecorder(), onTapUp: (x) => controller.stopRecorder(), onTapCancel: () => controller.stopRecorder(), child: Container( width: double.infinity, height: 44, decoration: BoxDecoration( color: Theme.of(context).colorScheme.primary, borderRadius: const BorderRadius.all(Radius.circular(6)), // boxShadow: const [ // BoxShadow( // color: Colors.grey, // offset: Offset(6.0, 6.0), //阴影x轴偏移量 // blurRadius: 10, //阴影模糊程度 // spreadRadius: 0 //阴影扩散程度 // ) // ] ), child: _speechButton(context), ), )), const SizedBox(height: 5) ], ); } Widget _speechButton(BuildContext context) { return Obx(() { switch (controller.state.status.value) { case SpeechStatus.idle: return Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.mic, color: Colors.white, size: 24,), const SizedBox(width: 5), Text(controller.state.btnTitle, style: Theme.of(context) .textTheme .bodyMedium ?.copyWith(color: Colors.white)) ],) ); case SpeechStatus.speaking: return Padding( padding: const EdgeInsets.only(left: 32, right: 32), child: Lottie.asset( 'assets/json/lottie-animation-voice-line.json', height: 44, fit: BoxFit.fill)); case SpeechStatus.thinking: return Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Lottie.asset('assets/json/lottie-animation-loading-white.json', width: 36, height: 36, fit: BoxFit.fill), const SizedBox(width: 10), Text('loading'.tr, style: Theme.of(context) .textTheme .bodyMedium ?.copyWith(color: Colors.white)) ], )); } }); } @override Widget build(BuildContext context) { return GetBuilder( builder: (_) { return Scaffold( appBar: AppBar(title: Text("im_chat_speech_assistant_title".tr)), body: SafeArea( child: Container( color: Theme.of(context).scaffoldBackgroundColor, child: Column( children: [ Expanded(flex: 1, child: _contentView(context)), _bottomBarView(context) ], ), ), ), ); }, ); } }