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

调整播放按钮的位置

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