提交 6f0554ab authored 作者: songchuancai's avatar songchuancai

调整语速

上级 ed9e49bf
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart'; import 'package:flutter_tts/flutter_tts.dart';
import '../models/chat_message.dart'; import '../models/chat_message.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import '../pallete.dart'; import '../pallete.dart';
class ChatBubble extends StatefulWidget { class ChatBubble extends StatefulWidget {
final ChatMessage message; final ChatMessage message;
final bool isTyping; final bool isTyping;
const ChatBubble({ const ChatBubble({
Key? key, Key? key,
required this.message, required this.message,
this.isTyping = false, this.isTyping = false,
}) : super(key: key); }) : super(key: key);
@override @override
State<ChatBubble> createState() => _ChatBubbleState(); State<ChatBubble> createState() => _ChatBubbleState();
} }
class _ChatBubbleState extends State<ChatBubble> { class _ChatBubbleState extends State<ChatBubble> {
final FlutterTts flutterTts = FlutterTts(); final FlutterTts flutterTts = FlutterTts();
bool isPlaying = false; bool isPlaying = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
flutterTts.setLanguage("zh-CN"); flutterTts.setLanguage("zh-CN");
// 设置语速 // 设置语速
flutterTts.setSpeechRate(2.5); flutterTts.setSpeechRate(2.5);
// 设置音调 // 设置音调
flutterTts.setPitch(1.0);
flutterTts.setPitch(0.8);
flutterTts.setCompletionHandler(() { flutterTts.setCompletionHandler(() {
if (mounted) { if (mounted) {
setState(() { setState(() {
isPlaying = false; isPlaying = false;
}); });
} }
}); });
flutterTts.setCancelHandler(() { flutterTts.setCancelHandler(() {
if (mounted) { if (mounted) {
setState(() { setState(() {
isPlaying = false; isPlaying = false;
}); });
} }
}); });
flutterTts.setErrorHandler((error) { flutterTts.setErrorHandler((error) {
if (mounted) { if (mounted) {
setState(() { setState(() {
isPlaying = false; isPlaying = false;
}); });
} }
}); });
} }
Future<void> _handlePlayStop() async { Future<void> _handlePlayStop() async {
if (isPlaying) { if (isPlaying) {
// 如果正在播放,则停止 // 如果正在播放,则停止
await flutterTts.stop(); await flutterTts.stop();
if (mounted) { if (mounted) {
setState(() { setState(() {
isPlaying = false; isPlaying = false;
}); });
} }
} else { } else {
// 如果未播放,则开始播放 // 如果未播放,则开始播放
if (mounted) { if (mounted) {
setState(() { setState(() {
isPlaying = true; isPlaying = true;
}); });
} }
try { try {
await flutterTts.speak(widget.message.text); await flutterTts.speak(widget.message.text);
} catch (e) { } catch (e) {
print('TTS Error: $e'); print('TTS Error: $e');
if (mounted) { if (mounted) {
setState(() { setState(() {
isPlaying = false; isPlaying = false;
}); });
} }
} }
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
alignment: widget.message.isUserMessage alignment: widget.message.isUserMessage
? Alignment.centerRight ? Alignment.centerRight
: Alignment.centerLeft, : Alignment.centerLeft,
child: Container( child: Container(
margin: const EdgeInsets.symmetric(vertical: 4), margin: const EdgeInsets.symmetric(vertical: 4),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7, maxWidth: MediaQuery.of(context).size.width * 0.7,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.message.isUserMessage color: widget.message.isUserMessage
? Pallete.firstSuggestionBoxColor ? Pallete.firstSuggestionBoxColor
: Pallete.assistantCircleColor, : Pallete.assistantCircleColor,
borderRadius: BorderRadius.circular(15).copyWith( borderRadius: BorderRadius.circular(15).copyWith(
bottomRight: widget.message.isUserMessage ? Radius.zero : null, bottomRight: widget.message.isUserMessage ? Radius.zero : null,
bottomLeft: !widget.message.isUserMessage ? Radius.zero : null, bottomLeft: !widget.message.isUserMessage ? Radius.zero : null,
), ),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
widget.message.isUserMessage widget.message.isUserMessage
? Text( ? Text(
widget.message.text, widget.message.text,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16, fontSize: 16,
), ),
) )
: MarkdownBody( : MarkdownBody(
data: widget.message.text, data: widget.message.text,
selectable: true, selectable: true,
styleSheet: MarkdownStyleSheet( styleSheet: MarkdownStyleSheet(
p: const TextStyle( p: const TextStyle(
color: Colors.black, color: Colors.black,
fontSize: 16, fontSize: 16,
), ),
code: const TextStyle( code: const TextStyle(
color: Colors.white, color: Colors.white,
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: 14, fontSize: 14,
height: 1.5, height: 1.5,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
), ),
codeblockPadding: const EdgeInsets.all(16), codeblockPadding: const EdgeInsets.all(16),
codeblockDecoration: BoxDecoration( codeblockDecoration: BoxDecoration(
color: const Color(0xFF1E1E1E), color: const Color(0xFF1E1E1E),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
blockquote: const TextStyle( blockquote: const TextStyle(
color: Colors.black87, color: Colors.black87,
fontSize: 16, fontSize: 16,
height: 1.5, height: 1.5,
), ),
blockquoteDecoration: BoxDecoration( blockquoteDecoration: BoxDecoration(
border: Border( border: Border(
left: BorderSide( left: BorderSide(
color: Colors.grey[300]!, color: Colors.grey[300]!,
width: 4, width: 4,
), ),
), ),
), ),
listBullet: const TextStyle(color: Colors.black87), listBullet: const TextStyle(color: Colors.black87),
), ),
), ),
if (!widget.message.isUserMessage && !widget.isTyping) ...[ if (!widget.message.isUserMessage && !widget.isTyping) ...[
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
IconButton( IconButton(
icon: Icon( icon: Icon(
isPlaying ? Icons.stop_circle : Icons.play_circle, isPlaying ? Icons.stop_circle : Icons.play_circle,
color: Colors.black87, color: Colors.black87,
size: 20, size: 20,
), ),
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: 32, minWidth: 32,
minHeight: 32, minHeight: 32,
), ),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: _handlePlayStop, onPressed: _handlePlayStop,
), ),
if (isPlaying) if (isPlaying)
Text( Text(
'正在播放...', '正在播放...',
style: TextStyle( style: TextStyle(
color: Colors.black87, color: Colors.black87,
fontSize: 12, fontSize: 12,
), ),
), ),
], ],
), ),
], ],
], ],
), ),
), ),
); );
} }
@override @override
void dispose() { void dispose() {
flutterTts.stop(); flutterTts.stop();
super.dispose(); super.dispose();
} }
} }
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论