新增 logger 打印方法
This commit is contained in:
@@ -27,7 +27,7 @@ class AIChatAssistantManager {
|
|||||||
|
|
||||||
AIChatAssistantManager._internal() {
|
AIChatAssistantManager._internal() {
|
||||||
// 初始化代码
|
// 初始化代码
|
||||||
debugPrint('AIChatAssistant 单例创建');
|
Logger.i('AIChatAssistant 单例创建');
|
||||||
_commandCubit = AIChatCommandCubit();
|
_commandCubit = AIChatCommandCubit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class AIChatAssistantManager {
|
|||||||
if (!file.existsSync()) {
|
if (!file.existsSync()) {
|
||||||
final data = await rootBundle.load(assetPath);
|
final data = await rootBundle.load(assetPath);
|
||||||
await file.writeAsBytes(data.buffer.asUint8List(), flush: true);
|
await file.writeAsBytes(data.buffer.asUint8List(), flush: true);
|
||||||
debugPrint('Asset "$assetPath" extracted to "${file.path}"');
|
Logger.i('Asset "$assetPath" extracted to "${file.path}"');
|
||||||
}
|
}
|
||||||
return file.path;
|
return file.path;
|
||||||
}
|
}
|
||||||
@@ -92,11 +92,11 @@ class AIChatAssistantManager {
|
|||||||
wakeWords: ['你好众众', '你好', '众众', '测试', '哈喽', '唤醒'], // 唤醒词列表
|
wakeWords: ['你好众众', '你好', '众众', '测试', '哈喽', '唤醒'], // 唤醒词列表
|
||||||
);
|
);
|
||||||
|
|
||||||
debugPrint('Starting Vosk Wakeword detection...');
|
Logger.i('Starting Vosk Wakeword detection...');
|
||||||
FlutterVoskWakeword.instance.start();
|
FlutterVoskWakeword.instance.start();
|
||||||
|
|
||||||
FlutterVoskWakeword.instance.recognitionStream.listen((event) {
|
FlutterVoskWakeword.instance.recognitionStream.listen((event) {
|
||||||
debugPrint('Vosk Wakeword detected: ${event.text}');
|
Logger.i('Vosk Wakeword detected: ${event.text}');
|
||||||
_wakeWordDetected = true;
|
_wakeWordDetected = true;
|
||||||
// 通知所有监听者
|
// 通知所有监听者
|
||||||
_wakeWordController.add(null);
|
_wakeWordController.add(null);
|
||||||
@@ -137,9 +137,9 @@ class AIChatAssistantManager {
|
|||||||
sensitivities: [0.6], // 可以调整灵敏度
|
sensitivities: [0.6], // 可以调整灵敏度
|
||||||
);
|
);
|
||||||
await _porcupineManager?.start();
|
await _porcupineManager?.start();
|
||||||
debugPrint("Porcupine manager started for wake-word detection.");
|
Logger.i("Porcupine manager started for wake-word detection.");
|
||||||
} on PorcupineException catch (e) {
|
} on PorcupineException catch (e) {
|
||||||
debugPrint("Failed to init Porcupine: ${e.message}");
|
Logger.e("Failed to init Porcupine: ${e.message}");
|
||||||
_isWakeWordEnabled = false;
|
_isWakeWordEnabled = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -147,7 +147,7 @@ class AIChatAssistantManager {
|
|||||||
await _porcupineManager?.delete();
|
await _porcupineManager?.delete();
|
||||||
_porcupineManager = null;
|
_porcupineManager = null;
|
||||||
_wakeWordDetected = false; // 关闭时重置状态
|
_wakeWordDetected = false; // 关闭时重置状态
|
||||||
debugPrint("Porcupine manager stopped and deleted.");
|
Logger.i("Porcupine manager stopped and deleted.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ class AIChatAssistantManager {
|
|||||||
// 我们只用了一个关键词,所以索引总是 0
|
// 我们只用了一个关键词,所以索引总是 0
|
||||||
if (keywordIndex == 0) {
|
if (keywordIndex == 0) {
|
||||||
_wakeWordDetected = true;
|
_wakeWordDetected = true;
|
||||||
debugPrint("Wake-word '众众' detected!");
|
Logger.i("Wake-word '众众' detected!");
|
||||||
// 通知所有监听者
|
// 通知所有监听者
|
||||||
_wakeWordController.add(null);
|
_wakeWordController.add(null);
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ class AIChatAssistantManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _porcupineErrorCallback(PorcupineException error) {
|
void _porcupineErrorCallback(PorcupineException error) {
|
||||||
debugPrint("Porcupine error: ${error.message}");
|
Logger.e("Porcupine error: ${error.message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 释放资源
|
/// 释放资源
|
||||||
@@ -178,6 +178,6 @@ class AIChatAssistantManager {
|
|||||||
_wakeWordController.close();
|
_wakeWordController.close();
|
||||||
setWakeWordDetection(false); // 确保 Porcupine 停止并释放
|
setWakeWordDetection(false); // 确保 Porcupine 停止并释放
|
||||||
FlutterVoskWakeword.instance.dispose();
|
FlutterVoskWakeword.instance.dispose();
|
||||||
debugPrint('AIChatAssistant 单例销毁');
|
Logger.i('AIChatAssistant 单例销毁');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:ai_chat_core/ai_chat_core.dart';
|
||||||
import 'package:record/record.dart';
|
import 'package:record/record.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@@ -16,11 +17,11 @@ class AudioRecorderService {
|
|||||||
|
|
||||||
Future<void> checkPermission() async {
|
Future<void> checkPermission() async {
|
||||||
final hasPermission = await _recorder.hasPermission();
|
final hasPermission = await _recorder.hasPermission();
|
||||||
print('麦克风权限状态: $hasPermission');
|
Logger.i('麦克风权限状态: $hasPermission');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startRecording() async {
|
Future<void> startRecording() async {
|
||||||
print('开始录音...');
|
Logger.i('开始录音...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 使用文件录音可能更稳定
|
// 使用文件录音可能更稳定
|
||||||
@@ -28,7 +29,7 @@ class AudioRecorderService {
|
|||||||
_tempFilePath =
|
_tempFilePath =
|
||||||
'${tempDir.path}/temp_audio_${DateTime.now().millisecondsSinceEpoch}.opus';
|
'${tempDir.path}/temp_audio_${DateTime.now().millisecondsSinceEpoch}.opus';
|
||||||
|
|
||||||
print('录音文件路径: $_tempFilePath');
|
Logger.i('录音文件路径: $_tempFilePath');
|
||||||
|
|
||||||
if (await _recorder.hasPermission()) {
|
if (await _recorder.hasPermission()) {
|
||||||
await _recorder.start(
|
await _recorder.start(
|
||||||
@@ -37,12 +38,12 @@ class AudioRecorderService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
_isRecording = true;
|
_isRecording = true;
|
||||||
print('录音已开始,使用OPUS格式,文件: $_tempFilePath');
|
Logger.i('录音已开始,使用OPUS格式,文件: $_tempFilePath');
|
||||||
} else {
|
} else {
|
||||||
print('没有麦克风权限,无法开始录音');
|
Logger.e('没有麦克风权限,无法开始录音');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('录音开始出错: $e');
|
Logger.e('录音开始出错: $e');
|
||||||
_isRecording = false;
|
_isRecording = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,30 +53,30 @@ class AudioRecorderService {
|
|||||||
|
|
||||||
_isRecording = false;
|
_isRecording = false;
|
||||||
|
|
||||||
print('停止录音...');
|
Logger.i('停止录音...');
|
||||||
try {
|
try {
|
||||||
final path = await _recorder.stop();
|
final path = await _recorder.stop();
|
||||||
print('录音已停止,文件路径: $path');
|
Logger.i('录音已停止,文件路径: $path');
|
||||||
|
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
final file = File(path);
|
final file = File(path);
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
final bytes = await file.readAsBytes();
|
final bytes = await file.readAsBytes();
|
||||||
print('读取到录音数据: ${bytes.length} 字节');
|
Logger.i('读取到录音数据: ${bytes.length} 字节');
|
||||||
|
|
||||||
if (bytes.isNotEmpty) {
|
if (bytes.isNotEmpty) {
|
||||||
return bytes;
|
return bytes;
|
||||||
} else {
|
} else {
|
||||||
print('录音数据为空');
|
Logger.e('录音数据为空');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print('录音文件不存在: $path');
|
Logger.e('录音文件不存在: $path');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print('录音路径为空');
|
Logger.e('录音路径为空');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('停止录音或发送过程出错: $e');
|
Logger.e('停止录音或发送过程出错: $e');
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// 负责SSE通信
|
// 负责SSE通信
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io' as IO;
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:ai_chat_assistant/ai_chat_assistant.dart';
|
||||||
|
|
||||||
import '../utils/tts_util.dart';
|
import '../utils/tts_util.dart';
|
||||||
|
|
||||||
import '../utils/common_util.dart';
|
import '../utils/common_util.dart';
|
||||||
@@ -14,8 +16,8 @@ class ChatSseService {
|
|||||||
String? _cachedUserId;
|
String? _cachedUserId;
|
||||||
String? _cachedConversationId;
|
String? _cachedConversationId;
|
||||||
|
|
||||||
HttpClient? _currentClient;
|
IO.HttpClient? _currentClient;
|
||||||
HttpClientResponse? _currentResponse;
|
IO.HttpClientResponse? _currentResponse;
|
||||||
bool _isAborted = true;
|
bool _isAborted = true;
|
||||||
bool _isFirstData = true;
|
bool _isFirstData = true;
|
||||||
bool _isTtsStarted = false;
|
bool _isTtsStarted = false;
|
||||||
@@ -30,11 +32,11 @@ class ChatSseService {
|
|||||||
required bool isChinese,
|
required bool isChinese,
|
||||||
required Function(String, String, bool) onStreamResponse,
|
required Function(String, String, bool) onStreamResponse,
|
||||||
}) async {
|
}) async {
|
||||||
print("----------------------SSE Start");
|
Logger.i("----------------------SSE Start");
|
||||||
_isAborted = false;
|
_isAborted = false;
|
||||||
if (_cachedUserId == null) {
|
if (_cachedUserId == null) {
|
||||||
_cachedUserId = _generateRandomUserId(6);
|
_cachedUserId = _generateRandomUserId(6);
|
||||||
print('初始化用户ID: $_cachedUserId');
|
Logger.i('初始化用户ID: $_cachedUserId');
|
||||||
}
|
}
|
||||||
String responseText = '';
|
String responseText = '';
|
||||||
StringBuffer buffer = StringBuffer();
|
StringBuffer buffer = StringBuffer();
|
||||||
@@ -80,7 +82,7 @@ class ChatSseService {
|
|||||||
// 只在达到完整句子时调用 TtsUtil.send
|
// 只在达到完整句子时调用 TtsUtil.send
|
||||||
for (final s in sentences) {
|
for (final s in sentences) {
|
||||||
String ttsStr=CommonUtil.cleanText(s, true);
|
String ttsStr=CommonUtil.cleanText(s, true);
|
||||||
// print("发送数据到TTS: $ttsStr");
|
// Logger.i("发送数据到TTS: $ttsStr");
|
||||||
TtsUtil.send(ttsStr);
|
TtsUtil.send(ttsStr);
|
||||||
}
|
}
|
||||||
// 缓存剩余不完整部分
|
// 缓存剩余不完整部分
|
||||||
@@ -90,7 +92,7 @@ class ChatSseService {
|
|||||||
buffer.write(remain);
|
buffer.write(remain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_currentClient = HttpClient();
|
_currentClient = IO.HttpClient();
|
||||||
try {
|
try {
|
||||||
final chatUri = Uri.parse('http://143.64.185.20:18606/chat');
|
final chatUri = Uri.parse('http://143.64.185.20:18606/chat');
|
||||||
final request = await _currentClient!.postUrl(chatUri);
|
final request = await _currentClient!.postUrl(chatUri);
|
||||||
@@ -114,17 +116,16 @@ class ChatSseService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (line.startsWith('data:')) {
|
if (line.startsWith('data:')) {
|
||||||
print('SSE line: $line');
|
Logger.i('SSE line: $line');
|
||||||
final jsonStr = line.substring(5).trim();
|
final jsonStr = line.substring(5).trim();
|
||||||
if (jsonStr == '[DONE]' || jsonStr.contains('message_end')) {
|
if (jsonStr == '[DONE]' || jsonStr.contains('message_end')) {
|
||||||
TtsUtil.complete();
|
TtsUtil.complete();
|
||||||
onStreamResponse(messageId, responseText, true);
|
onStreamResponse(messageId, responseText, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
print('SSE json: $jsonStr');
|
|
||||||
try {
|
try {
|
||||||
final jsonData = json.decode(jsonStr);
|
final jsonData = json.decode(jsonStr);
|
||||||
print('SSE jsonData: $jsonData');
|
Logger.i('SSE jsonData: $jsonData');
|
||||||
if (jsonData.containsKey('conversation_id') &&
|
if (jsonData.containsKey('conversation_id') &&
|
||||||
_cachedConversationId == null) {
|
_cachedConversationId == null) {
|
||||||
_cachedConversationId = jsonData['conversation_id'];
|
_cachedConversationId = jsonData['conversation_id'];
|
||||||
@@ -141,12 +142,12 @@ class ChatSseService {
|
|||||||
onStreamResponse(messageId, responseText, false);
|
onStreamResponse(messageId, responseText, false);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('解析 SSE 数据出错: $e, 原始数据: $jsonStr');
|
Logger.e('解析 SSE 数据出错: $e, 原始数据: $jsonStr');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print('SSE 连接失败,状态码: ${_currentResponse!.statusCode}');
|
Logger.e('SSE 连接失败,状态码: ${_currentResponse!.statusCode}');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// todo
|
// todo
|
||||||
@@ -156,7 +157,7 @@ class ChatSseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startTts(bool isChinese) async {
|
Future<void> startTts(bool isChinese) async {
|
||||||
print("----------------------TTS Start");
|
Logger.i("----------------------TTS Start");
|
||||||
if (!_isTtsStarted) {
|
if (!_isTtsStarted) {
|
||||||
if (await TtsUtil.start(isChinese) == true) {
|
if (await TtsUtil.start(isChinese) == true) {
|
||||||
_isTtsStarted = true;
|
_isTtsStarted = true;
|
||||||
@@ -193,7 +194,7 @@ class ChatSseService {
|
|||||||
void resetSession() {
|
void resetSession() {
|
||||||
_cachedUserId = null;
|
_cachedUserId = null;
|
||||||
_cachedConversationId = null;
|
_cachedConversationId = null;
|
||||||
print('SSE会话已重置');
|
Logger.i('SSE会话已重置');
|
||||||
}
|
}
|
||||||
|
|
||||||
String _generateRandomUserId(int length) {
|
String _generateRandomUserId(int length) {
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ class CommandService {
|
|||||||
try {
|
try {
|
||||||
return await onCommandReceived!(type, params);
|
return await onCommandReceived!(type, params);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('执行命令出错: $e');
|
Logger.e('执行命令出错: $e');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print('警告: 命令处理回调未注册');
|
Logger.w('警告: 命令处理回调未注册');
|
||||||
}
|
}
|
||||||
return (false, null);
|
return (false, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ class VehicleCommandService {
|
|||||||
.toList();
|
.toList();
|
||||||
return VehicleCommandResponse(tips: tips, commands: vehicleCommandList);
|
return VehicleCommandResponse(tips: tips, commands: vehicleCommandList);
|
||||||
} else {
|
} else {
|
||||||
print(
|
Logger.e(
|
||||||
'Vehicle command query failed: ${response.statusCode}, ${response.body}');
|
'Vehicle command query failed: ${response.statusCode}, ${response.body}');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error during vehicle command processing: $e');
|
Logger.e('Error during vehicle command processing: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,10 +50,10 @@ class VehicleCommandService {
|
|||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return response.body;
|
return response.body;
|
||||||
} else {
|
} else {
|
||||||
print("请求控制回复失败: ${response.statusCode}");
|
Logger.e("请求控制回复失败: ${response.statusCode}");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('请求控制回复异常: $e');
|
Logger.e('请求控制回复异常: $e');
|
||||||
}
|
}
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:ai_chat_core/ai_chat_core.dart';
|
||||||
import 'package:t_basic_intl/intl.dart';
|
import 'package:t_basic_intl/intl.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ class LocationService {
|
|||||||
return json.decode(response.body)['address'];
|
return json.decode(response.body)['address'];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error during text classification: $e');
|
Logger.e('Error during text classification: $e');
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
_latestUserMessageId = addMessage("", true, MessageStatus.listening);
|
_latestUserMessageId = addMessage("", true, MessageStatus.listening);
|
||||||
_asrChannel.invokeMethod("startAsr");
|
_asrChannel.invokeMethod("startAsr");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('录音开始出错: $e');
|
Logger.e('录音开始出错: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ class MessageService extends ChangeNotifier {
|
|||||||
changeState(MessageServiceState.replying);
|
changeState(MessageServiceState.replying);
|
||||||
await reply(recognizedText);
|
await reply(recognizedText);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
Logger.e(e.toString());
|
||||||
} finally {
|
} finally {
|
||||||
changeState(MessageServiceState.idle);
|
changeState(MessageServiceState.idle);
|
||||||
_asrCompleter = null;
|
_asrCompleter = null;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'package:ai_chat_assistant/utils/common_util.dart';
|
import 'package:ai_chat_assistant/utils/common_util.dart';
|
||||||
|
import 'package:ai_chat_core/ai_chat_core.dart';
|
||||||
|
|
||||||
// ...existing code...
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_tts/flutter_tts.dart';
|
import 'package:flutter_tts/flutter_tts.dart';
|
||||||
|
|
||||||
@@ -46,27 +46,27 @@ class LocalTtsService {
|
|||||||
|
|
||||||
// 设置TTS事件回调
|
// 设置TTS事件回调
|
||||||
_flutterTts.setStartHandler(() {
|
_flutterTts.setStartHandler(() {
|
||||||
print('TTS开始播放');
|
Logger.i('TTS开始播放');
|
||||||
_isPlaying = true;
|
_isPlaying = true;
|
||||||
_onStartHandler?.call();
|
_onStartHandler?.call();
|
||||||
});
|
});
|
||||||
|
|
||||||
_flutterTts.setCompletionHandler(() {
|
_flutterTts.setCompletionHandler(() {
|
||||||
print('TTS播放完成');
|
Logger.i('TTS播放完成');
|
||||||
_isPlaying = false;
|
_isPlaying = false;
|
||||||
_handleTtsPlaybackComplete();
|
_handleTtsPlaybackComplete();
|
||||||
});
|
});
|
||||||
|
|
||||||
_flutterTts.setErrorHandler((msg) {
|
_flutterTts.setErrorHandler((msg) {
|
||||||
print('TTS错误: $msg');
|
Logger.e('TTS错误: $msg');
|
||||||
_isPlaying = false;
|
_isPlaying = false;
|
||||||
_onErrorHandler?.call(msg);
|
_onErrorHandler?.call(msg);
|
||||||
_handleTtsPlaybackComplete(); // 错误时也要处理下一个任务
|
_handleTtsPlaybackComplete(); // 错误时也要处理下一个任务
|
||||||
});
|
});
|
||||||
|
|
||||||
print('TTS引擎初始化完成');
|
Logger.i('TTS引擎初始化完成');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('TTS引擎初始化失败: $e');
|
Logger.e('TTS引擎初始化失败: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,17 +78,17 @@ class LocalTtsService {
|
|||||||
try {
|
try {
|
||||||
// 获取可用的TTS引擎
|
// 获取可用的TTS引擎
|
||||||
dynamic engines = await _flutterTts.getEngines;
|
dynamic engines = await _flutterTts.getEngines;
|
||||||
print('可用的TTS引擎: $engines');
|
Logger.i('可用的TTS引擎: $engines');
|
||||||
|
|
||||||
// 获取可用的语言
|
// 获取可用的语言
|
||||||
dynamic languages = await _flutterTts.getLanguages;
|
dynamic languages = await _flutterTts.getLanguages;
|
||||||
print('支持的语言: $languages');
|
Logger.i('支持的语言: $languages');
|
||||||
|
|
||||||
// 获取当前默认引擎
|
// 获取当前默认引擎
|
||||||
dynamic defaultEngine = await _flutterTts.getDefaultEngine;
|
dynamic defaultEngine = await _flutterTts.getDefaultEngine;
|
||||||
print('当前默认引擎: $defaultEngine');
|
Logger.i('当前默认引擎: $defaultEngine');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('获取TTS引擎信息失败: $e');
|
Logger.e('获取TTS引擎信息失败: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,19 +100,19 @@ class LocalTtsService {
|
|||||||
_isProcessingTasks = false;
|
_isProcessingTasks = false;
|
||||||
_streamCompleted = false;
|
_streamCompleted = false;
|
||||||
_isPlaying = false; // 重置播放状态
|
_isPlaying = false; // 重置播放状态
|
||||||
print('所有TTS队列和缓存已清空');
|
Logger.i('所有TTS队列和缓存已清空');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 推送文本到TTS队列进行流式处理(保证顺序)
|
/// 推送文本到TTS队列进行流式处理(保证顺序)
|
||||||
void pushTextForStreamTTS(String text) {
|
void pushTextForStreamTTS(String text) {
|
||||||
if (text.trim().isEmpty) {
|
if (text.trim().isEmpty) {
|
||||||
print('接收到空字符串,跳过推送到TTS队列');
|
Logger.i('接收到空字符串,跳过推送到TTS队列');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String cleanedText = CommonUtil.cleanText(text, true);
|
String cleanedText = CommonUtil.cleanText(text, true);
|
||||||
if (cleanedText.isEmpty) {
|
if (cleanedText.isEmpty) {
|
||||||
print('清理后的文本为空,跳过推送到TTS队列');
|
Logger.i('清理后的文本为空,跳过推送到TTS队列');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ class LocalTtsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
_orderedTasks.add(task);
|
_orderedTasks.add(task);
|
||||||
print('添加TTS任务 (索引: $taskIndex): $cleanedText');
|
Logger.i('添加TTS任务 (索引: $taskIndex): $cleanedText');
|
||||||
|
|
||||||
// 尝试处理队列中的下一个任务
|
// 尝试处理队列中的下一个任务
|
||||||
_processNextReadyTask();
|
_processNextReadyTask();
|
||||||
@@ -135,7 +135,7 @@ class LocalTtsService {
|
|||||||
void _processNextReadyTask() {
|
void _processNextReadyTask() {
|
||||||
// 如果正在播放,则不处理下一个任务
|
// 如果正在播放,则不处理下一个任务
|
||||||
if (_isPlaying) {
|
if (_isPlaying) {
|
||||||
print('当前正在播放音频,等待播放完成');
|
Logger.i('当前正在播放音频,等待播放完成');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ class LocalTtsService {
|
|||||||
while (_nextTaskIndex < _orderedTasks.length) {
|
while (_nextTaskIndex < _orderedTasks.length) {
|
||||||
TtsTask task = _orderedTasks[_nextTaskIndex];
|
TtsTask task = _orderedTasks[_nextTaskIndex];
|
||||||
if (task.status == TtsTaskStatus.ready) {
|
if (task.status == TtsTaskStatus.ready) {
|
||||||
print('按顺序播放TTS (索引: ${task.index}): ${task.text}');
|
Logger.i('按顺序播放TTS (索引: ${task.index}): ${task.text}');
|
||||||
_playTts(task.text);
|
_playTts(task.text);
|
||||||
return; // 等待当前音频播放完成
|
return; // 等待当前音频播放完成
|
||||||
} else {
|
} else {
|
||||||
@@ -155,7 +155,7 @@ class LocalTtsService {
|
|||||||
|
|
||||||
// 检查是否所有任务都已处理完
|
// 检查是否所有任务都已处理完
|
||||||
if (_streamCompleted && _nextTaskIndex >= _orderedTasks.length) {
|
if (_streamCompleted && _nextTaskIndex >= _orderedTasks.length) {
|
||||||
print('=== 所有TTS任务播放完成 ===');
|
Logger.i('=== 所有TTS任务播放完成 ===');
|
||||||
_isProcessingTasks = false;
|
_isProcessingTasks = false;
|
||||||
_onCompletionHandler?.call();
|
_onCompletionHandler?.call();
|
||||||
}
|
}
|
||||||
@@ -164,28 +164,28 @@ class LocalTtsService {
|
|||||||
/// 播放TTS
|
/// 播放TTS
|
||||||
Future<void> _playTts(String text) async {
|
Future<void> _playTts(String text) async {
|
||||||
if (_isPlaying) {
|
if (_isPlaying) {
|
||||||
print('已有TTS正在播放,跳过本次播放');
|
Logger.i('已有TTS正在播放,跳过本次播放');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
print('开始播放TTS: $text');
|
Logger.i('开始播放TTS: $text');
|
||||||
// 检测语言并设置
|
// 检测语言并设置
|
||||||
bool isChinese = CommonUtil.containChinese(text);
|
bool isChinese = CommonUtil.containChinese(text);
|
||||||
String targetLanguage = isChinese ? "zh-CN" : "en-US";
|
String targetLanguage = isChinese ? "zh-CN" : "en-US";
|
||||||
// 检查语言是否可用,如果不可用则使用默认语言
|
// 检查语言是否可用,如果不可用则使用默认语言
|
||||||
bool isLanguageOk = await isLanguageAvailable(targetLanguage);
|
bool isLanguageOk = await isLanguageAvailable(targetLanguage);
|
||||||
if (!isLanguageOk) {
|
if (!isLanguageOk) {
|
||||||
print('语言 $targetLanguage 不可用,使用默认语言');
|
Logger.i('语言 $targetLanguage 不可用,使用默认语言');
|
||||||
// 可以在这里设置备用语言或保持当前语言
|
// 可以在这里设置备用语言或保持当前语言
|
||||||
} else {
|
} else {
|
||||||
await _flutterTts.setLanguage(targetLanguage);
|
await _flutterTts.setLanguage(targetLanguage);
|
||||||
print('设置TTS语言为: $targetLanguage');
|
Logger.i('设置TTS语言为: $targetLanguage');
|
||||||
}
|
}
|
||||||
// 播放TTS
|
// 播放TTS
|
||||||
await _flutterTts.speak(text);
|
await _flutterTts.speak(text);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('播放TTS失败: $e');
|
Logger.i('播放TTS失败: $e');
|
||||||
// 播放失败,重置状态并继续下一个
|
// 播放失败,重置状态并继续下一个
|
||||||
_isPlaying = false;
|
_isPlaying = false;
|
||||||
_handleTtsPlaybackComplete();
|
_handleTtsPlaybackComplete();
|
||||||
@@ -195,7 +195,7 @@ class LocalTtsService {
|
|||||||
/// 处理TTS播放完成
|
/// 处理TTS播放完成
|
||||||
void _handleTtsPlaybackComplete() {
|
void _handleTtsPlaybackComplete() {
|
||||||
if (!_isPlaying) {
|
if (!_isPlaying) {
|
||||||
print('TTS已停止播放,处理下一个任务');
|
Logger.i('TTS已停止播放,处理下一个任务');
|
||||||
// 标记当前任务为已完成
|
// 标记当前任务为已完成
|
||||||
if (_nextTaskIndex < _orderedTasks.length &&
|
if (_nextTaskIndex < _orderedTasks.length &&
|
||||||
_orderedTasks[_nextTaskIndex].status == TtsTaskStatus.ready) {
|
_orderedTasks[_nextTaskIndex].status == TtsTaskStatus.ready) {
|
||||||
@@ -209,7 +209,7 @@ class LocalTtsService {
|
|||||||
|
|
||||||
/// 标记流完成
|
/// 标记流完成
|
||||||
void markSSEStreamCompleted() {
|
void markSSEStreamCompleted() {
|
||||||
print('=== 标记SSE流完成 ===');
|
Logger.i('=== 标记SSE流完成 ===');
|
||||||
_streamCompleted = true;
|
_streamCompleted = true;
|
||||||
|
|
||||||
// 尝试完成剩余任务
|
// 尝试完成剩余任务
|
||||||
@@ -218,7 +218,7 @@ class LocalTtsService {
|
|||||||
|
|
||||||
/// 停止播放
|
/// 停止播放
|
||||||
void stopSSEPlayback() {
|
void stopSSEPlayback() {
|
||||||
print("停止SSE播放");
|
Logger.i("停止SSE播放");
|
||||||
// 立即停止TTS播放,无论当前是否在播报
|
// 立即停止TTS播放,无论当前是否在播报
|
||||||
_flutterTts.stop();
|
_flutterTts.stop();
|
||||||
_isPlaying = false;
|
_isPlaying = false;
|
||||||
@@ -241,11 +241,11 @@ class LocalTtsService {
|
|||||||
// 保留原有的播放逻辑用于完整文本处理
|
// 保留原有的播放逻辑用于完整文本处理
|
||||||
Future<void> processCompleteText(String text) async {
|
Future<void> processCompleteText(String text) async {
|
||||||
if (text.trim().isEmpty) {
|
if (text.trim().isEmpty) {
|
||||||
print("文本为空,跳过TTS处理");
|
Logger.i("文本为空,跳过TTS处理");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
print("开始处理完整文本TTS,原始文本长度: ${text.length}字");
|
Logger.i("开始处理完整文本TTS,原始文本长度: ${text.length}字");
|
||||||
|
|
||||||
// 清理缓存和重置队列
|
// 清理缓存和重置队列
|
||||||
clearAll();
|
clearAll();
|
||||||
@@ -261,23 +261,23 @@ class LocalTtsService {
|
|||||||
Future<void> _processCompleteTextAsStream(String text) async {
|
Future<void> _processCompleteTextAsStream(String text) async {
|
||||||
// 清理markdown和异常字符
|
// 清理markdown和异常字符
|
||||||
String cleanedText = _cleanTextForTts(text);
|
String cleanedText = _cleanTextForTts(text);
|
||||||
print("清理后文本长度: ${cleanedText.length}字");
|
Logger.i("清理后文本长度: ${cleanedText.length}字");
|
||||||
|
|
||||||
if (cleanedText.trim().isEmpty) {
|
if (cleanedText.trim().isEmpty) {
|
||||||
print("清理后文本为空,跳过TTS");
|
Logger.i("清理后文本为空,跳过TTS");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分割成句子并逐个处理
|
// 分割成句子并逐个处理
|
||||||
List<String> sentences = _splitTextIntoSentences(cleanedText);
|
List<String> sentences = _splitTextIntoSentences(cleanedText);
|
||||||
print("=== 文本分段完成,共 ${sentences.length} 个句子 ===");
|
Logger.i("=== 文本分段完成,共 ${sentences.length} 个句子 ===");
|
||||||
|
|
||||||
// 推送每个句子到流式TTS处理
|
// 推送每个句子到流式TTS处理
|
||||||
for (int i = 0; i < sentences.length; i++) {
|
for (int i = 0; i < sentences.length; i++) {
|
||||||
final sentence = sentences[i].trim();
|
final sentence = sentences[i].trim();
|
||||||
if (sentence.isEmpty) continue;
|
if (sentence.isEmpty) continue;
|
||||||
|
|
||||||
print("处理句子 ${i + 1}/${sentences.length}: $sentence");
|
Logger.i("处理句子 ${i + 1}/${sentences.length}: $sentence");
|
||||||
pushTextForStreamTTS(sentence);
|
pushTextForStreamTTS(sentence);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,19 +288,19 @@ class LocalTtsService {
|
|||||||
// 停止当前播放的辅助方法
|
// 停止当前播放的辅助方法
|
||||||
Future<void> _stopCurrentPlayback() async {
|
Future<void> _stopCurrentPlayback() async {
|
||||||
try {
|
try {
|
||||||
print("停止当前播放");
|
Logger.i("停止当前播放");
|
||||||
|
|
||||||
if (_isPlaying) {
|
if (_isPlaying) {
|
||||||
await _flutterTts.stop();
|
await _flutterTts.stop();
|
||||||
_isPlaying = false;
|
_isPlaying = false;
|
||||||
print("TTS.stop() 调用完成");
|
Logger.i("TTS.stop() 调用完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 短暂延迟确保播放器状态稳定
|
// 短暂延迟确保播放器状态稳定
|
||||||
await Future.delayed(Duration(milliseconds: 300));
|
await Future.delayed(Duration(milliseconds: 300));
|
||||||
print("播放器状态稳定延迟完成");
|
Logger.i("播放器状态稳定延迟完成");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('停止当前播放错误: $e');
|
Logger.i('停止当前播放错误: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ class LocalTtsService {
|
|||||||
await processCompleteText(text);
|
await processCompleteText(text);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('TTS 播放错误: $e');
|
Logger.i('TTS 播放错误: $e');
|
||||||
_onErrorHandler?.call('TTS 播放错误: $e');
|
_onErrorHandler?.call('TTS 播放错误: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -469,7 +469,7 @@ class LocalTtsService {
|
|||||||
await _stopCurrentPlayback();
|
await _stopCurrentPlayback();
|
||||||
clearAll();
|
clearAll();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('停止音频播放错误: $e');
|
Logger.i('停止音频播放错误: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ class LocalTtsService {
|
|||||||
await _flutterTts.pause();
|
await _flutterTts.pause();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('暂停音频播放错误: $e');
|
Logger.i('暂停音频播放错误: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,9 +487,9 @@ class LocalTtsService {
|
|||||||
try {
|
try {
|
||||||
// flutter_tts没有resume方法,需要重新播放当前文本
|
// flutter_tts没有resume方法,需要重新播放当前文本
|
||||||
// 这里可以根据需要实现
|
// 这里可以根据需要实现
|
||||||
print('TTS不支持恢复,需要重新播放');
|
Logger.i('TTS不支持恢复,需要重新播放');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('恢复音频播放错误: $e');
|
Logger.i('恢复音频播放错误: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,7 +537,7 @@ class LocalTtsService {
|
|||||||
dynamic engines = await _flutterTts.getEngines;
|
dynamic engines = await _flutterTts.getEngines;
|
||||||
return engines ?? [];
|
return engines ?? [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('获取TTS引擎列表失败: $e');
|
Logger.i('获取TTS引擎列表失败: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,10 +546,10 @@ class LocalTtsService {
|
|||||||
Future<bool> setEngine(String engineName) async {
|
Future<bool> setEngine(String engineName) async {
|
||||||
try {
|
try {
|
||||||
int result = await _flutterTts.setEngine(engineName);
|
int result = await _flutterTts.setEngine(engineName);
|
||||||
print('设置TTS引擎: $engineName, 结果: $result');
|
Logger.i('设置TTS引擎: $engineName, 结果: $result');
|
||||||
return result == 1; // 1表示成功
|
return result == 1; // 1表示成功
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('设置TTS引擎失败: $e');
|
Logger.i('设置TTS引擎失败: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,7 +560,7 @@ class LocalTtsService {
|
|||||||
dynamic engine = await _flutterTts.getDefaultEngine;
|
dynamic engine = await _flutterTts.getDefaultEngine;
|
||||||
return engine?.toString();
|
return engine?.toString();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('获取当前TTS引擎失败: $e');
|
Logger.i('获取当前TTS引擎失败: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,7 +571,7 @@ class LocalTtsService {
|
|||||||
dynamic languages = await _flutterTts.getLanguages;
|
dynamic languages = await _flutterTts.getLanguages;
|
||||||
return languages ?? [];
|
return languages ?? [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('获取支持的语言列表失败: $e');
|
Logger.i('获取支持的语言列表失败: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,7 +582,7 @@ class LocalTtsService {
|
|||||||
dynamic result = await _flutterTts.isLanguageAvailable(language);
|
dynamic result = await _flutterTts.isLanguageAvailable(language);
|
||||||
return result == true;
|
return result == true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('检查语言可用性失败: $e');
|
Logger.i('检查语言可用性失败: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:ai_chat_core/ai_chat_core.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
@@ -7,9 +8,9 @@ class VoiceRecognitionService {
|
|||||||
Future<String?> recognizeSpeech(List<int> audioBytes,
|
Future<String?> recognizeSpeech(List<int> audioBytes,
|
||||||
{String lang = 'cn'}) async {
|
{String lang = 'cn'}) async {
|
||||||
try {
|
try {
|
||||||
print('准备发送OPUS音频数据,大小: ${audioBytes.length} 字节');
|
Logger.i('准备发送OPUS音频数据,大小: ${audioBytes.length} 字节');
|
||||||
final uri = Uri.parse('http://143.64.185.20:18606/voice');
|
final uri = Uri.parse('http://143.64.185.20:18606/voice');
|
||||||
print('发送到: $uri');
|
Logger.i('发送到: $uri');
|
||||||
|
|
||||||
final request = http.MultipartRequest('POST', uri);
|
final request = http.MultipartRequest('POST', uri);
|
||||||
request.files.add(
|
request.files.add(
|
||||||
@@ -22,26 +23,26 @@ class VoiceRecognitionService {
|
|||||||
);
|
);
|
||||||
request.fields['lang'] = lang; // 根据参数设置语言
|
request.fields['lang'] = lang; // 根据参数设置语言
|
||||||
|
|
||||||
print('发送请求...');
|
Logger.i('发送请求...');
|
||||||
final streamResponse = await request.send();
|
final streamResponse = await request.send();
|
||||||
print('收到响应,状态码: ${streamResponse.statusCode}');
|
Logger.i('收到响应,状态码: ${streamResponse.statusCode}');
|
||||||
|
|
||||||
final response = await http.Response.fromStream(streamResponse);
|
final response = await http.Response.fromStream(streamResponse);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
print('响应内容: ${response.body}');
|
Logger.i('响应内容: ${response.body}');
|
||||||
final text = _parseTextFromJson(response.body);
|
final text = _parseTextFromJson(response.body);
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
print('解析出文本: $text');
|
Logger.i('解析出文本: $text');
|
||||||
return text; // 返回识别的文本
|
return text; // 返回识别的文本
|
||||||
} else {
|
} else {
|
||||||
print('解析文本失败');
|
Logger.i('解析文本失败');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print('请求失败,状态码: ${response.statusCode},响应: ${response.body}');
|
Logger.i('请求失败,状态码: ${response.statusCode},响应: ${response.body}');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('发送录音到服务器时出错: $e');
|
Logger.i('发送录音到服务器时出错: $e');
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -55,7 +56,7 @@ class VoiceRecognitionService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('JSON解析错误: $e, 原始数据: $body');
|
Logger.i('JSON解析错误: $e, 原始数据: $body');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import '../enums/vehicle_command_type.dart';
|
import '../enums/vehicle_command_type.dart';
|
||||||
|
import '../utils/logger.dart';
|
||||||
|
|
||||||
/// 车辆控制命令类
|
/// 车辆控制命令类
|
||||||
class VehicleCommand {
|
class VehicleCommand {
|
||||||
@@ -22,8 +23,6 @@ class VehicleCommand {
|
|||||||
orElse: () => VehicleCommandType.unknown,
|
orElse: () => VehicleCommandType.unknown,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error parsing command string: $e');
|
|
||||||
// 默认为搜车指令,或者你可以选择抛出异常
|
|
||||||
type = VehicleCommandType.unknown;
|
type = VehicleCommandType.unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ dependencies:
|
|||||||
fluttertoast: ^8.2.12
|
fluttertoast: ^8.2.12
|
||||||
record: ^6.0.0
|
record: ^6.0.0
|
||||||
http: ^1.5.0
|
http: ^1.5.0
|
||||||
|
http_parser: ^4.0.3
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
flutter_markdown : ^0.7.7
|
flutter_markdown : ^0.7.7
|
||||||
flutter_markdown_plus : ^1.0.1
|
flutter_markdown_plus : ^1.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user