import 'package:flutter/material.dart'; import '../theme.dart'; /// 微信式语音消息:气泡 + 下方独立白底转写框。 class VoiceMessageBlock extends StatelessWidget { const VoiceMessageBlock({ super.key, required this.mine, required this.seconds, required this.playing, this.transcript, this.loading = false, this.onTap, this.onLongPress, this.bubbleColor, this.iconColor, this.readLabel, this.bottomMargin = 8, }); final bool mine; final int seconds; final bool playing; final String? transcript; final bool loading; final VoidCallback? onTap; final VoidCallback? onLongPress; final Color? bubbleColor; final Color? iconColor; final String? readLabel; final double bottomMargin; @override Widget build(BuildContext context) { final bubble = mine ? (bubbleColor ?? kBubbleMine) : Colors.white; final ink = iconColor ?? kInk; final width = (80 + seconds * 4).clamp(80, 180).toDouble(); return Column( crossAxisAlignment: mine ? CrossAxisAlignment.end : CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ GestureDetector( onTap: onTap, onLongPress: onLongPress, child: Container( margin: EdgeInsets.only(bottom: bottomMargin), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9), constraints: BoxConstraints(maxWidth: 280, minWidth: width), decoration: BoxDecoration( color: bubble, borderRadius: BorderRadius.circular(6), ), child: Column( crossAxisAlignment: mine ? CrossAxisAlignment.end : CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: width, child: Row( mainAxisAlignment: mine ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ if (!mine) ...[ Icon( playing ? Icons.stop_circle_outlined : Icons.volume_up_outlined, size: 20, color: ink, ), const SizedBox(width: 6), Text('$seconds″', style: TextStyle(fontSize: 16, color: ink)), ] else ...[ Text('$seconds″', style: TextStyle(fontSize: 16, color: ink)), const SizedBox(width: 6), Icon( playing ? Icons.stop_circle_outlined : Icons.volume_up_outlined, size: 20, color: ink, ), ], ], ), ), if (readLabel != null) Text(readLabel!, style: const TextStyle(fontSize: 10, color: kMute)), ], ), ), ), if (loading) _transcriptBox(const Text('转文字中…', style: TextStyle(fontSize: 14, color: kMute))) else if (transcript != null && transcript!.isNotEmpty) _transcriptBox(Text( transcript!, style: const TextStyle(fontSize: 15, color: kInk, height: 1.35), )), ], ); } Widget _transcriptBox(Widget child) { return Container( margin: EdgeInsets.only(bottom: bottomMargin), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), constraints: const BoxConstraints(maxWidth: 280), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(6), border: Border.all(color: const Color(0xFFE5E5E5)), ), child: child, ); } }