feat: 修复了插件需要外部Provider的问题;加上了一个简易的车控状态返回
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,3 +43,5 @@ app.*.map.json
|
|||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
.vscode/
|
||||||
@@ -1,9 +1,48 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:ai_chat_assistant/app.dart';
|
import 'package:ai_chat_assistant/ai_chat_assistant.dart';
|
||||||
import 'package:ai_chat_assistant/services/message_service.dart';
|
|
||||||
import 'package:ai_chat_assistant/enums/vehicle_command_type.dart';
|
class VehicleCommandClent extends VehicleCommandHandler {
|
||||||
|
@override
|
||||||
|
AIChatCommandCubit? get commandCubit => super.commandCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<(bool, Map<String, dynamic>?)> executeCommand(VehicleCommand command) async {
|
||||||
|
// 在这里实现具体的车控命令执行逻辑
|
||||||
|
print('执行车控命令: ${command.type}, 参数: ${command.params}');
|
||||||
|
|
||||||
|
if (commandCubit != null) {
|
||||||
|
|
||||||
|
commandCubit?.emit(AIChatCommandState(
|
||||||
|
commandId: command.commandId,
|
||||||
|
commandType: command.type,
|
||||||
|
params: command.params,
|
||||||
|
status: AIChatCommandStatus.executing,
|
||||||
|
timestamp: DateTime.now(),
|
||||||
|
errorMessage: null,
|
||||||
|
result: null,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟命令执行完成
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
|
||||||
|
if (commandCubit != null) {
|
||||||
|
commandCubit?.emit(AIChatCommandState(
|
||||||
|
commandId: command.commandId,
|
||||||
|
commandType: command.type,
|
||||||
|
params: command.params,
|
||||||
|
status: AIChatCommandStatus.success,
|
||||||
|
timestamp: DateTime.now(),
|
||||||
|
errorMessage: null,
|
||||||
|
result: {'message': '命令执行成功'},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.value((true, {'message': '命令已执行'}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@@ -14,20 +53,17 @@ void main() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化 AI Chat Assistant,注册车控命令回调
|
// 初始化 AI Chat Assistant,注册车控命令回调
|
||||||
ChatAssistantApp.initialize(
|
// ChatAssistantApp.initialize(
|
||||||
commandCallback: (VehicleCommandType type, Map<String, dynamic>? params) async {
|
// commandCallback: (VehicleCommandType type, Map<String, dynamic>? params) async {
|
||||||
// 这里是示例的车控命令处理逻辑
|
// // 这里是示例的车控命令处理逻辑
|
||||||
print('收到车控命令: $type, 参数: $params');
|
// print('收到车控命令: $type, 参数: $params');
|
||||||
return Future.value((true, {'message': '命令已执行'}));
|
// return Future.value((true, {'message': '命令已执行'}));
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
|
|
||||||
runApp(
|
AIChatAssistantManager.instance.setupCommandHandle(commandHandler: VehicleCommandClent());
|
||||||
ChangeNotifierProvider(
|
|
||||||
create: (_) => MessageService(),
|
runApp(const MyApp());
|
||||||
child: const MyApp(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
|||||||
26
lib/ai_chat_assistant.dart
Normal file
26
lib/ai_chat_assistant.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
// widgets
|
||||||
|
export 'screens/full_screen.dart';
|
||||||
|
export 'screens/part_screen.dart';
|
||||||
|
export 'app.dart';
|
||||||
|
export 'widgets/floating_icon.dart';
|
||||||
|
|
||||||
|
// easy bloc
|
||||||
|
export 'bloc/easy_bloc.dart';
|
||||||
|
export 'bloc/ai_chat_cubit.dart';
|
||||||
|
export 'bloc/command_state.dart';
|
||||||
|
|
||||||
|
// services
|
||||||
|
export 'manager.dart';
|
||||||
|
export 'services/message_service.dart';
|
||||||
|
export 'services/command_service.dart';
|
||||||
|
|
||||||
|
// types && enums && models
|
||||||
|
export 'models/vehicle_cmd.dart';
|
||||||
|
export 'models/vehicle_cmd_response.dart';
|
||||||
|
export 'models/chat_message.dart';
|
||||||
|
export 'models/vehicle_status_info.dart';
|
||||||
|
|
||||||
|
export 'enums/vehicle_command_type.dart';
|
||||||
|
export 'enums/message_status.dart';
|
||||||
|
export 'enums/message_service_state.dart';
|
||||||
24
lib/bloc/ai_chat_cubit.dart
Normal file
24
lib/bloc/ai_chat_cubit.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import '../enums/vehicle_command_type.dart';
|
||||||
|
import '../services/command_service.dart';
|
||||||
|
import 'easy_bloc.dart';
|
||||||
|
import 'command_state.dart';
|
||||||
|
|
||||||
|
class AIChatCommandCubit extends EasyCubit<AIChatCommandState> {
|
||||||
|
AIChatCommandCubit() : super(const AIChatCommandState());
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
void reset() {
|
||||||
|
emit(const AIChatCommandState());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成唯一命令ID
|
||||||
|
String _generateCommandId() {
|
||||||
|
return '${DateTime.now().millisecondsSinceEpoch}_${state.commandType?.name ?? 'unknown'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前是否有命令在执行
|
||||||
|
bool get isExecuting => state.status == AIChatCommandStatus.executing;
|
||||||
|
|
||||||
|
// 获取当前命令ID
|
||||||
|
String? get currentCommandId => state.commandId;
|
||||||
|
}
|
||||||
68
lib/bloc/command_state.dart
Normal file
68
lib/bloc/command_state.dart
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import '../enums/vehicle_command_type.dart';
|
||||||
|
|
||||||
|
// AI Chat Command States
|
||||||
|
enum AIChatCommandStatus {
|
||||||
|
idle,
|
||||||
|
executing,
|
||||||
|
success,
|
||||||
|
failure,
|
||||||
|
cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
class AIChatCommandState {
|
||||||
|
final String? commandId;
|
||||||
|
final VehicleCommandType? commandType;
|
||||||
|
final Map<String, dynamic>? params;
|
||||||
|
final AIChatCommandStatus status;
|
||||||
|
final String? errorMessage;
|
||||||
|
final Map<String, dynamic>? result;
|
||||||
|
final DateTime? timestamp;
|
||||||
|
|
||||||
|
const AIChatCommandState({
|
||||||
|
this.commandId,
|
||||||
|
this.commandType,
|
||||||
|
this.params,
|
||||||
|
this.status = AIChatCommandStatus.idle,
|
||||||
|
this.errorMessage,
|
||||||
|
this.result,
|
||||||
|
this.timestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
AIChatCommandState copyWith({
|
||||||
|
String? commandId,
|
||||||
|
VehicleCommandType? commandType,
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
AIChatCommandStatus? status,
|
||||||
|
String? errorMessage,
|
||||||
|
Map<String, dynamic>? result,
|
||||||
|
DateTime? timestamp,
|
||||||
|
}) {
|
||||||
|
return AIChatCommandState(
|
||||||
|
commandId: commandId ?? this.commandId,
|
||||||
|
commandType: commandType ?? this.commandType,
|
||||||
|
params: params ?? this.params,
|
||||||
|
status: status ?? this.status,
|
||||||
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
|
result: result ?? this.result,
|
||||||
|
timestamp: timestamp ?? this.timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
return other is AIChatCommandState &&
|
||||||
|
other.commandId == commandId &&
|
||||||
|
other.commandType == commandType &&
|
||||||
|
other.status == status &&
|
||||||
|
other.errorMessage == errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return commandId.hashCode ^
|
||||||
|
commandType.hashCode ^
|
||||||
|
status.hashCode ^
|
||||||
|
errorMessage.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
lib/bloc/easy_bloc.dart
Normal file
78
lib/bloc/easy_bloc.dart
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
// 简易的Bloc, 简易的状态管理
|
||||||
|
|
||||||
|
abstract class EasyBlocBase<State> {
|
||||||
|
|
||||||
|
EasyBlocBase(this.state): assert(state != null);
|
||||||
|
|
||||||
|
final StreamController<State> _stateController = StreamController<State>.broadcast();
|
||||||
|
|
||||||
|
Stream<State> get stream => _stateController.stream;
|
||||||
|
State state;
|
||||||
|
|
||||||
|
bool _emitted = false;
|
||||||
|
|
||||||
|
// 添加监听器方法
|
||||||
|
StreamSubscription<State> listen(
|
||||||
|
void Function(State state) onData, {
|
||||||
|
Function? onError,
|
||||||
|
void Function()? onDone,
|
||||||
|
bool? cancelOnError,
|
||||||
|
}) {
|
||||||
|
return stream.listen(
|
||||||
|
onData,
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新状态
|
||||||
|
void emit(State newState) {
|
||||||
|
if (_stateController.isClosed) return;
|
||||||
|
if (newState == state && _emitted) return;
|
||||||
|
this.state = newState;
|
||||||
|
_stateController.add(state);
|
||||||
|
_emitted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mustCallSuper
|
||||||
|
Future<void> close() async {
|
||||||
|
await _stateController.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EasyBloc<Event, State> extends EasyBlocBase<State> {
|
||||||
|
EasyBloc(State initialState) : super(initialState) {
|
||||||
|
_eventController.stream.listen(_mapEventToState);
|
||||||
|
}
|
||||||
|
|
||||||
|
final StreamController<Event> _eventController = StreamController<Event>();
|
||||||
|
|
||||||
|
// 子类需要实现这个方法来处理事件
|
||||||
|
@mustBeOverridden
|
||||||
|
void _mapEventToState(Event event) {
|
||||||
|
// 默认不做任何处理,如果不实现会抛出异常
|
||||||
|
throw UnimplementedError('_mapEventToState must be implemented by subclasses');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加事件
|
||||||
|
void add(Event event) {
|
||||||
|
if (_eventController.isClosed) return;
|
||||||
|
_eventController.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _eventController.close();
|
||||||
|
await super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EasyCubit<State> extends EasyBlocBase<State> {
|
||||||
|
EasyCubit(State initialState) : super(initialState);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,4 +3,4 @@ enum MessageServiceState {
|
|||||||
recording,
|
recording,
|
||||||
recognizing,
|
recognizing,
|
||||||
replying,
|
replying,
|
||||||
}
|
}
|
||||||
52
lib/manager.dart
Normal file
52
lib/manager.dart
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'bloc/ai_chat_cubit.dart';
|
||||||
|
import 'bloc/command_state.dart';
|
||||||
|
import 'models/vehicle_cmd.dart';
|
||||||
|
|
||||||
|
/// 车辆命令处理器抽象类
|
||||||
|
abstract class VehicleCommandHandler {
|
||||||
|
AIChatCommandCubit? commandCubit;
|
||||||
|
/// 执行车辆控制命令
|
||||||
|
Future<(bool, Map<String, dynamic>?)> executeCommand(VehicleCommand command);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AIChatAssistantManager {
|
||||||
|
static final AIChatAssistantManager _instance = AIChatAssistantManager._internal();
|
||||||
|
|
||||||
|
static AIChatAssistantManager get instance => _instance;
|
||||||
|
|
||||||
|
AIChatAssistantManager._internal() {
|
||||||
|
// 初始化代码
|
||||||
|
debugPrint('AIChatAssistant 单例创建');
|
||||||
|
_commandCubit = AIChatCommandCubit();
|
||||||
|
}
|
||||||
|
|
||||||
|
AIChatCommandCubit? _commandCubit;
|
||||||
|
|
||||||
|
VehicleCommandHandler? commandClient;
|
||||||
|
|
||||||
|
/// 初始化命令处理器
|
||||||
|
void setupCommandHandle({
|
||||||
|
required VehicleCommandHandler commandHandler,
|
||||||
|
}) {
|
||||||
|
commandClient = commandHandler;
|
||||||
|
commandClient?.commandCubit = _commandCubit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取命令状态流
|
||||||
|
Stream<AIChatCommandState> get commandStateStream {
|
||||||
|
if (_commandCubit == null) {
|
||||||
|
throw StateError('AIChatAssistant 未初始化');
|
||||||
|
}
|
||||||
|
return _commandCubit!.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 释放资源
|
||||||
|
void dispose() {
|
||||||
|
_commandCubit?.close();
|
||||||
|
_commandCubit = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import '../enums/vehicle_command_type.dart';
|
import '../enums/vehicle_command_type.dart';
|
||||||
|
|
||||||
|
/// 车辆控制命令类
|
||||||
class VehicleCommand {
|
class VehicleCommand {
|
||||||
final VehicleCommandType type;
|
final VehicleCommandType type;
|
||||||
final Map<String, dynamic>? params;
|
final Map<String, dynamic>? params;
|
||||||
final String error;
|
final String error;
|
||||||
|
late String commandId;
|
||||||
|
|
||||||
VehicleCommand({required this.type, this.params, this.error = ''});
|
VehicleCommand({required this.type, this.params, this.error = ''}) {
|
||||||
|
commandId = DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
|
}
|
||||||
|
|
||||||
// 从字符串创建命令(用于从API响应解析)
|
// 从字符串创建命令(用于从API响应解析)
|
||||||
factory VehicleCommand.fromString(
|
factory VehicleCommand.fromString(
|
||||||
|
|||||||
@@ -57,10 +57,16 @@ class _PartScreenState extends State<PartScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _openFullScreen() async {
|
void _openFullScreen() async {
|
||||||
|
final messageService = context.read<MessageService>();
|
||||||
|
|
||||||
widget.onHide?.call();
|
widget.onHide?.call();
|
||||||
|
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const FullScreen(),
|
builder: (context) => ChangeNotifierProvider.value(
|
||||||
|
value: messageService, // 传递同一个单例实例
|
||||||
|
child: const FullScreen(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import '../models/vehicle_status_info.dart';
|
|||||||
typedef CommandCallback = Future<(bool, Map<String, dynamic>? params)> Function(
|
typedef CommandCallback = Future<(bool, Map<String, dynamic>? params)> Function(
|
||||||
VehicleCommandType type, Map<String, dynamic>? params);
|
VehicleCommandType type, Map<String, dynamic>? params);
|
||||||
|
|
||||||
/// 命令处理器类 - 负责处理来自AI的车辆控制命令
|
/// 命令处理器类 - 负责处理来自AI的车辆控制命令(使用回调的方式交给App去实现具体的车控逻辑)
|
||||||
class CommandService {
|
class CommandService {
|
||||||
/// 保存主应用注册的回调函数
|
/// 保存主应用注册的回调函数
|
||||||
static CommandCallback? onCommandReceived;
|
static CommandCallback? onCommandReceived;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import '../models/vehicle_cmd.dart';
|
|||||||
import '../models/vehicle_cmd_response.dart';
|
import '../models/vehicle_cmd_response.dart';
|
||||||
import 'vehicle_state_service.dart';
|
import 'vehicle_state_service.dart';
|
||||||
|
|
||||||
|
/// 车辆命令服务 - 负责与后端交互以获取和处理车辆控制命令
|
||||||
class VehicleCommandService {
|
class VehicleCommandService {
|
||||||
// final VehicleStateService vehicleStateService = VehicleStateService();
|
// final VehicleStateService vehicleStateService = VehicleStateService();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:ai_chat_assistant/ai_chat_assistant.dart';
|
||||||
import 'package:ai_chat_assistant/utils/common_util.dart';
|
import 'package:ai_chat_assistant/utils/common_util.dart';
|
||||||
import 'package:ai_chat_assistant/utils/tts_util.dart';
|
import 'package:ai_chat_assistant/utils/tts_util.dart';
|
||||||
import 'package:basic_intl/intl.dart';
|
import 'package:basic_intl/intl.dart';
|
||||||
@@ -7,11 +8,6 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import '../enums/vehicle_command_type.dart';
|
|
||||||
import '../enums/message_service_state.dart';
|
|
||||||
import '../enums/message_status.dart';
|
|
||||||
import '../models/chat_message.dart';
|
|
||||||
import '../models/vehicle_cmd.dart';
|
|
||||||
import '../services/chat_sse_service.dart';
|
import '../services/chat_sse_service.dart';
|
||||||
import '../services/classification_service.dart';
|
import '../services/classification_service.dart';
|
||||||
import '../services/control_recognition_service.dart';
|
import '../services/control_recognition_service.dart';
|
||||||
@@ -20,48 +16,72 @@ import '../services/control_recognition_service.dart';
|
|||||||
import 'command_service.dart';
|
import 'command_service.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
// 用单例的模式创建
|
||||||
class MessageService extends ChangeNotifier {
|
class MessageService extends ChangeNotifier {
|
||||||
static const MethodChannel _asrChannel = MethodChannel('com.example.ai_chat_assistant/ali_sdk');
|
static const MethodChannel _asrChannel = MethodChannel('com.example.ai_chat_assistant/ali_sdk');
|
||||||
|
|
||||||
static final MessageService _instance = MessageService._internal();
|
static MessageService? _instance;
|
||||||
|
|
||||||
factory MessageService() => _instance;
|
static MessageService get instance {
|
||||||
|
return _instance ??= MessageService._internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提供工厂构造函数供 Provider 使用
|
||||||
|
factory MessageService() => instance;
|
||||||
|
|
||||||
Completer<String>? _asrCompleter;
|
Completer<String>? _asrCompleter;
|
||||||
|
|
||||||
MessageService._internal() {
|
MessageService._internal() {
|
||||||
_asrChannel.setMethodCallHandler((call) async {
|
// 注册MethodChannel的handler
|
||||||
switch (call.method) {
|
_asrChannel.setMethodCallHandler(_handleMethodCall);
|
||||||
case "onAsrResult":
|
}
|
||||||
replaceMessage(
|
|
||||||
id: _latestUserMessageId!,
|
@override
|
||||||
text: call.arguments,
|
void dispose() {
|
||||||
status: MessageStatus.normal);
|
// 取消注册MethodChannel的handler,避免内存泄漏和意外回调
|
||||||
break;
|
_asrChannel.setMethodCallHandler(null);
|
||||||
case "onAsrStop":
|
_asrCompleter?.completeError('Disposed');
|
||||||
int index = findMessageIndexById(_latestUserMessageId!);
|
_asrCompleter = null;
|
||||||
if (index == -1) {
|
super.dispose();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
final message = _messages[index];
|
// 提供真正的销毁方法(可选,用于应用退出时)
|
||||||
if (message.text.isEmpty) {
|
void destroyInstance() {
|
||||||
removeMessageById(_latestUserMessageId!);
|
_asrChannel.setMethodCallHandler(null);
|
||||||
return;
|
_asrCompleter?.completeError('Destroyed');
|
||||||
}
|
_asrCompleter = null;
|
||||||
if (_asrCompleter != null && !_asrCompleter!.isCompleted) {
|
super.dispose();
|
||||||
_asrCompleter!.complete(messages.last.text);
|
_instance = null;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
Future<dynamic> _handleMethodCall(MethodCall call) async {
|
||||||
});
|
switch (call.method) {
|
||||||
|
case "onAsrResult":
|
||||||
|
replaceMessage(
|
||||||
|
id: _latestUserMessageId!, text: call.arguments, status: MessageStatus.normal);
|
||||||
|
break;
|
||||||
|
case "onAsrStop":
|
||||||
|
int index = findMessageIndexById(_latestUserMessageId!);
|
||||||
|
if (index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final message = _messages[index];
|
||||||
|
if (message.text.isEmpty) {
|
||||||
|
removeMessageById(_latestUserMessageId!);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_asrCompleter != null && !_asrCompleter!.isCompleted) {
|
||||||
|
_asrCompleter!.complete(messages.last.text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final ChatSseService _chatSseService = ChatSseService();
|
final ChatSseService _chatSseService = ChatSseService();
|
||||||
// final LocalTtsService _ttsService = LocalTtsService();
|
// final LocalTtsService _ttsService = LocalTtsService();
|
||||||
// final AudioRecorderService _audioService = AudioRecorderService();
|
// final AudioRecorderService _audioService = AudioRecorderService();
|
||||||
// final VoiceRecognitionService _recognitionService = VoiceRecognitionService();
|
// final VoiceRecognitionService _recognitionService = VoiceRecognitionService();
|
||||||
final TextClassificationService _classificationService =
|
final TextClassificationService _classificationService = TextClassificationService();
|
||||||
TextClassificationService();
|
|
||||||
final VehicleCommandService _vehicleCommandService = VehicleCommandService();
|
final VehicleCommandService _vehicleCommandService = VehicleCommandService();
|
||||||
|
|
||||||
final List<ChatMessage> _messages = [];
|
final List<ChatMessage> _messages = [];
|
||||||
@@ -122,11 +142,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
String addMessage(String text, bool isUser, MessageStatus status) {
|
String addMessage(String text, bool isUser, MessageStatus status) {
|
||||||
String uuid = Uuid().v1();
|
String uuid = Uuid().v1();
|
||||||
_messages.add(ChatMessage(
|
_messages.add(ChatMessage(
|
||||||
id: uuid,
|
id: uuid, text: text, isUser: isUser, timestamp: DateTime.now(), status: status));
|
||||||
text: text,
|
|
||||||
isUser: isUser,
|
|
||||||
timestamp: DateTime.now(),
|
|
||||||
status: status));
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
@@ -226,8 +242,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
if (_isReplyAborted) {
|
if (_isReplyAborted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final vehicleCommandResponse =
|
final vehicleCommandResponse = await _vehicleCommandService.getCommandFromText(text);
|
||||||
await _vehicleCommandService.getCommandFromText(text);
|
|
||||||
if (vehicleCommandResponse == null) {
|
if (vehicleCommandResponse == null) {
|
||||||
if (_isReplyAborted) {
|
if (_isReplyAborted) {
|
||||||
return;
|
return;
|
||||||
@@ -235,10 +250,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
String msg = isChinese
|
String msg = isChinese
|
||||||
? "无法识别车辆控制命令,请重试"
|
? "无法识别车辆控制命令,请重试"
|
||||||
: "Cannot recognize the vehicle control command, please try again";
|
: "Cannot recognize the vehicle control command, please try again";
|
||||||
replaceMessage(
|
replaceMessage(id: _latestAssistantMessageId!, text: msg, status: MessageStatus.normal);
|
||||||
id: _latestAssistantMessageId!,
|
|
||||||
text: msg,
|
|
||||||
status: MessageStatus.normal);
|
|
||||||
} else {
|
} else {
|
||||||
if (_isReplyAborted) {
|
if (_isReplyAborted) {
|
||||||
return;
|
return;
|
||||||
@@ -259,8 +271,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
if (_isReplyAborted) {
|
if (_isReplyAborted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (command.type == VehicleCommandType.unknown ||
|
if (command.type == VehicleCommandType.unknown || command.error.isNotEmpty) {
|
||||||
command.error.isNotEmpty) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (command.type == VehicleCommandType.openAC) {
|
if (command.type == VehicleCommandType.openAC) {
|
||||||
@@ -268,8 +279,8 @@ class MessageService extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
bool isSuccess;
|
bool isSuccess;
|
||||||
if (containOpenAC && command.type == VehicleCommandType.changeACTemp) {
|
if (containOpenAC && command.type == VehicleCommandType.changeACTemp) {
|
||||||
isSuccess = await Future.delayed(const Duration(milliseconds: 2000),
|
isSuccess = await Future.delayed(
|
||||||
() => processCommand(command, isChinese));
|
const Duration(milliseconds: 2000), () => processCommand(command, isChinese));
|
||||||
} else {
|
} else {
|
||||||
isSuccess = await processCommand(command, isChinese);
|
isSuccess = await processCommand(command, isChinese);
|
||||||
}
|
}
|
||||||
@@ -285,8 +296,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
if (_isReplyAborted || successCommandList.isEmpty) {
|
if (_isReplyAborted || successCommandList.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String controlResponse =
|
String controlResponse = await _vehicleCommandService.getControlResponse(successCommandList);
|
||||||
await _vehicleCommandService.getControlResponse(successCommandList);
|
|
||||||
if (_isReplyAborted || controlResponse.isEmpty) {
|
if (_isReplyAborted || controlResponse.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -297,20 +307,22 @@ class MessageService extends ChangeNotifier {
|
|||||||
Future<bool> processCommand(VehicleCommand command, bool isChinese) async {
|
Future<bool> processCommand(VehicleCommand command, bool isChinese) async {
|
||||||
String msg = "";
|
String msg = "";
|
||||||
MessageStatus status = MessageStatus.normal;
|
MessageStatus status = MessageStatus.normal;
|
||||||
final (isSuccess, result) = await CommandService.executeCommand(
|
var commandObjc = VehicleCommand(type: command.type, params: command.params);
|
||||||
command.type,
|
var client = AIChatAssistantManager.instance.commandClient;
|
||||||
params: command.params);
|
if (client == null) {
|
||||||
|
msg = isChinese ? "车辆控制服务未初始化" : "Vehicle control service is not initialized";
|
||||||
|
status = MessageStatus.failure;
|
||||||
|
addMessage(msg, false, status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final (isSuccess, result) = await client.executeCommand(commandObjc);
|
||||||
if (command.type == VehicleCommandType.locateCar) {
|
if (command.type == VehicleCommandType.locateCar) {
|
||||||
msg = isChinese
|
msg = isChinese ? "很抱歉,定位车辆位置失败" : "Sorry, locate the vehicle unsuccessfully";
|
||||||
? "很抱歉,定位车辆位置失败"
|
|
||||||
: "Sorry, locate the vehicle unsuccessfully";
|
|
||||||
status = MessageStatus.failure;
|
status = MessageStatus.failure;
|
||||||
if (isSuccess && result != null && result.isNotEmpty) {
|
if (isSuccess && result != null && result.isNotEmpty) {
|
||||||
String address = result['address'];
|
String address = result['address'];
|
||||||
if (address.isNotEmpty) {
|
if (address.isNotEmpty) {
|
||||||
msg = isChinese
|
msg = isChinese ? "已为您找到车辆位置:\"$address\"" : "The vehicle location is \"$address\"";
|
||||||
? "已为您找到车辆位置:\"$address\""
|
|
||||||
: "The vehicle location is \"$address\"";
|
|
||||||
status = MessageStatus.success;
|
status = MessageStatus.success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,8 +398,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
status: MessageStatus.completed,
|
status: MessageStatus.completed,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
replaceMessage(
|
replaceMessage(id: messageId, text: responseText, status: MessageStatus.thinking);
|
||||||
id: messageId, text: responseText, status: MessageStatus.thinking);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
abortReply();
|
abortReply();
|
||||||
@@ -401,8 +412,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
if (index == -1 || messages[index].status != MessageStatus.thinking) {
|
if (index == -1 || messages[index].status != MessageStatus.thinking) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
replaceMessage(
|
replaceMessage(id: _latestAssistantMessageId!, status: MessageStatus.aborted);
|
||||||
id: _latestAssistantMessageId!, status: MessageStatus.aborted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeMessageById(String id) {
|
void removeMessageById(String id) {
|
||||||
|
|||||||
@@ -96,9 +96,14 @@ class _FloatingIconState extends State<FloatingIcon> with TickerProviderStateMix
|
|||||||
// 显示全屏界面
|
// 显示全屏界面
|
||||||
void _showFullScreen() async {
|
void _showFullScreen() async {
|
||||||
_hidePartScreen();
|
_hidePartScreen();
|
||||||
|
final messageService = context.read<MessageService>();
|
||||||
|
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const FullScreen(),
|
builder: (context) => ChangeNotifierProvider.value(
|
||||||
|
value: messageService, // 传递同一个单例实例
|
||||||
|
child: const FullScreen(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -164,7 +169,11 @@ class _FloatingIconState extends State<FloatingIcon> with TickerProviderStateMix
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
return ChangeNotifierProvider(create: (_) => MessageService(), child: _buildFloatingIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFloatingIcon() {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
if (_isShowPartScreen)
|
if (_isShowPartScreen)
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.11.1"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||||
@@ -496,5 +496,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.6.2 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.27.0"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
meta: ^1.15.0
|
||||||
fluttertoast: ^8.2.12
|
fluttertoast: ^8.2.12
|
||||||
record: ^6.0.0
|
record: ^6.0.0
|
||||||
http: ^1.4.0
|
http: ^1.4.0
|
||||||
|
|||||||
Reference in New Issue
Block a user