提交 ee9639f1 authored 作者: songchuancai's avatar songchuancai

增加语音播放的按钮

上级 23ffd7b4
......@@ -30,6 +30,8 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'pages/apps_page.dart';
import 'pages/chat_bubble.dart';
class HomePage extends StatefulWidget {
final String? customTitle;
......@@ -266,7 +268,7 @@ class _HomePageState extends State<HomePage> {
}
}
await systemSpeak(fullResponse);
//await systemSpeak(fullResponse);
await _updateCurrentConversation();
} catch (e) {
......@@ -323,7 +325,7 @@ class _HomePageState extends State<HomePage> {
}
}
await systemSpeak(fullResponse);
// await systemSpeak(fullResponse);
await _updateCurrentConversation();
} catch (e) {
......@@ -791,75 +793,7 @@ class _HomePageState extends State<HomePage> {
return Column(
children: [
Align(
alignment: message.isUserMessage
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.symmetric(vertical: 4),
padding: const EdgeInsets.all(12),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
decoration: BoxDecoration(
color: message.isUserMessage
? Pallete.firstSuggestionBoxColor
: Pallete.assistantCircleColor,
borderRadius: BorderRadius.circular(15).copyWith(
bottomRight:
message.isUserMessage ? Radius.zero : null,
bottomLeft:
!message.isUserMessage ? Radius.zero : null,
),
),
child: message.isUserMessage
? Text(
message.text,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
)
: MarkdownBody(
data: message.text,
selectable: true,
styleSheet: MarkdownStyleSheet(
p: const TextStyle(
color: Colors.black,
fontSize: 16,
),
code: const TextStyle(
color: Colors.white,
fontFamily: 'monospace',
fontSize: 14,
height: 1.5,
backgroundColor: Colors.transparent,
),
codeblockPadding:
const EdgeInsets.all(16),
codeblockDecoration: BoxDecoration(
color: const Color(0xFF1E1E1E),
borderRadius: BorderRadius.circular(8),
),
blockquote: const TextStyle(
color: Colors.black87,
fontSize: 16,
height: 1.5,
),
blockquoteDecoration: BoxDecoration(
border: Border(
left: BorderSide(
color: Colors.grey[300]!,
width: 4,
),
),
),
listBullet: const TextStyle(
color: Colors.black87),
),
),
),
),
ChatBubble(message: message),
if (_isLoading &&
index == messages.length - 1 &&
message.isUserMessage)
......
class ChatMessage {
final String text;
final bool isUserMessage;
final DateTime timestamp;
final bool isMarkdown;
ChatMessage({
required this.text,
required this.isUserMessage,
DateTime? timestamp,
this.isMarkdown = true,
}) : timestamp = timestamp ?? DateTime.now();
Map<String, dynamic> toJson() => {
'text': text,
'isUserMessage': isUserMessage,
'timestamp': timestamp.toIso8601String(),
'isMarkdown': isMarkdown,
};
factory ChatMessage.fromJson(Map<String, dynamic> json) => ChatMessage(
text: json['text'],
isUserMessage: json['isUserMessage'],
timestamp: DateTime.parse(json['timestamp']),
isMarkdown: json['isMarkdown'],
);
}
class ChatMessage {
final String text;
final bool isUserMessage;
final DateTime timestamp;
final bool isMarkdown;
ChatMessage({
required this.text,
required this.isUserMessage,
DateTime? timestamp,
this.isMarkdown = true,
}) : timestamp = timestamp ?? DateTime.now();
Map<String, dynamic> toJson() => {
'text': text,
'isUserMessage': isUserMessage,
'timestamp': timestamp.toIso8601String(),
'isMarkdown': isMarkdown,
};
factory ChatMessage.fromJson(Map<String, dynamic> json) => ChatMessage(
text: json['text'],
isUserMessage: json['isUserMessage'],
timestamp: DateTime.parse(json['timestamp']),
isMarkdown: json['isMarkdown'],
);
}
\ No newline at end of file
......
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
import '../models/chat_message.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../pallete.dart';
class ChatBubble extends StatefulWidget {
final ChatMessage message;
const ChatBubble({
Key? key,
required this.message,
}) : super(key: key);
@override
State<ChatBubble> createState() => _ChatBubbleState();
}
class _ChatBubbleState extends State<ChatBubble> {
final FlutterTts flutterTts = FlutterTts();
bool isPlaying = false;
@override
void initState() {
super.initState();
flutterTts.setLanguage("zh-CN");
// 设置语速
flutterTts.setSpeechRate(2.5);
// 设置音调
flutterTts.setPitch(0.8);
flutterTts.setCompletionHandler(() {
if (mounted) {
setState(() {
isPlaying = false;
});
}
});
flutterTts.setCancelHandler(() {
if (mounted) {
setState(() {
isPlaying = false;
});
}
});
flutterTts.setErrorHandler((error) {
if (mounted) {
setState(() {
isPlaying = false;
});
}
});
}
Future<void> _handlePlayStop() async {
if (isPlaying) {
// 如果正在播放,则停止
await flutterTts.stop();
if (mounted) {
setState(() {
isPlaying = false;
});
}
} else {
// 如果未播放,则开始播放
if (mounted) {
setState(() {
isPlaying = true;
});
}
try {
await flutterTts.speak(widget.message.text);
} catch (e) {
print('TTS Error: $e');
if (mounted) {
setState(() {
isPlaying = false;
});
}
}
}
}
@override
Widget build(BuildContext context) {
return Align(
alignment: widget.message.isUserMessage
? Alignment.centerRight
: Alignment.centerLeft,
child: Column(
crossAxisAlignment: widget.message.isUserMessage
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 4),
padding: const EdgeInsets.all(12),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
decoration: BoxDecoration(
color: widget.message.isUserMessage
? Pallete.firstSuggestionBoxColor
: Pallete.assistantCircleColor,
borderRadius: BorderRadius.circular(15).copyWith(
bottomRight: widget.message.isUserMessage ? Radius.zero : null,
bottomLeft: !widget.message.isUserMessage ? Radius.zero : null,
),
),
child: widget.message.isUserMessage
? Text(
widget.message.text,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
)
: MarkdownBody(
data: widget.message.text,
selectable: true,
styleSheet: MarkdownStyleSheet(
p: const TextStyle(
color: Colors.black,
fontSize: 16,
),
code: const TextStyle(
color: Colors.white,
fontFamily: 'monospace',
fontSize: 14,
height: 1.5,
backgroundColor: Colors.transparent,
),
codeblockPadding: const EdgeInsets.all(16),
codeblockDecoration: BoxDecoration(
color: const Color(0xFF1E1E1E),
borderRadius: BorderRadius.circular(8),
),
blockquote: const TextStyle(
color: Colors.black87,
fontSize: 16,
height: 1.5,
),
blockquoteDecoration: BoxDecoration(
border: Border(
left: BorderSide(
color: Colors.grey[300]!,
width: 4,
),
),
),
listBullet: const TextStyle(color: Colors.black87),
),
),
),
if (!widget.message.isUserMessage)
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(
isPlaying ? Icons.stop_circle : Icons.play_circle,
color: Pallete.firstSuggestionBoxColor,
size: 20,
),
constraints: const BoxConstraints(
minWidth: 32,
minHeight: 32,
),
padding: EdgeInsets.zero,
onPressed: _handlePlayStop,
),
if (isPlaying)
Text(
'正在播放...',
style: TextStyle(
color: Pallete.firstSuggestionBoxColor,
fontSize: 12,
),
),
],
),
),
],
),
);
}
@override
void dispose() {
flutterTts.stop();
super.dispose();
}
}
......@@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
}
......@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
......
......@@ -5,12 +5,16 @@
import FlutterMacOS
import Foundation
import audioplayers_darwin
import flutter_tts
import path_provider_foundation
import shared_preferences_foundation
import speech_to_text_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
FlutterTtsPlugin.register(with: registry.registrar(forPlugin: "FlutterTtsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SpeechToTextMacosPlugin.register(with: registry.registrar(forPlugin: "SpeechToTextMacosPlugin"))
}
......@@ -25,6 +25,62 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.11.0"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
sha256: c05c6147124cd63e725e861335a8b4d57300b80e6e92cea7c145c739223bbaef
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.2.1"
audioplayers_android:
dependency: transitive
description:
name: audioplayers_android
sha256: b00e1a0e11365d88576320ec2d8c192bc21f1afb6c0e5995d1c57ae63156acb5
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.3"
audioplayers_darwin:
dependency: transitive
description:
name: audioplayers_darwin
sha256: "3034e99a6df8d101da0f5082dcca0a2a99db62ab1d4ddb3277bed3f6f81afe08"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.2"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
sha256: "60787e73fefc4d2e0b9c02c69885402177e818e4e27ef087074cf27c02246c9e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
sha256: "365c547f1bb9e77d94dd1687903a668d8f7ac3409e48e6e6a3668a1ac2982adb"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.0"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
sha256: "22cd0173e54d92bd9b2c80b1204eb1eb159ece87475ab58c9788a70ec43c2a62"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.0"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
sha256: "9536812c9103563644ada2ef45ae523806b0745f7a78e89d1b5fb1951de90e1a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
boolean_selector:
dependency: transitive
description:
......@@ -180,10 +236,10 @@ packages:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.1"
version: "0.6.7"
json_annotation:
dependency: transitive
description:
......@@ -264,6 +320,30 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.0"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.12"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
......@@ -385,10 +465,10 @@ packages:
dependency: "direct main"
description:
name: speech_to_text
sha256: "97425fd8cc60424061a0584b6c418c0eedab5201cc5e96ef15a946d7fab7b9b7"
sha256: "57fef1d41bdebe298e84842c89bb4ac91f31cdbec7830c8cb1fc6b91d03abd42"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.6.2"
version: "6.6.0"
speech_to_text_macos:
dependency: transitive
description:
......@@ -437,6 +517,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
......
......@@ -43,6 +43,7 @@ dependencies:
flutter_markdown: ^0.6.18
flutter_highlight: ^0.7.0
markdown: ^7.1.1
audioplayers: ^5.2.1
dev_dependencies:
flutter_test:
......
......@@ -6,9 +6,12 @@
#include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.h>
#include <flutter_tts/flutter_tts_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
FlutterTtsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterTtsPlugin"));
}
......@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows
flutter_tts
)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论