build method
chat bubble builder method
Implementation
@override
Widget build(BuildContext context) {
bool stateTick = false;
Icon? stateIcon;
if (sent) {
stateTick = true;
stateIcon = Icon(
Icons.done,
size: 18,
color: Color(0xFF97AD8E),
);
}
if (delivered) {
stateTick = true;
stateIcon = Icon(
Icons.done_all,
size: 18,
color: Color(0xFF97AD8E),
);
}
if (seen) {
stateTick = true;
stateIcon = Icon(
Icons.done_all,
size: 18,
color: Color(0xFF92DEDA),
);
}
final bool showStatusArea = stateTick || timestamp != null || isEdited;
final Color forwardedColor =
(textStyle.color ?? Colors.black87).withOpacity(0.6);
final double dur = duration ?? 0.0;
final double pos = position ?? 0.0;
final double waveProgress = dur > 0 ? (pos / dur).clamp(0.0, 1.0) : 0.0;
return Row(
children: <Widget>[
isSender
? Expanded(
child: SizedBox(
width: 5,
),
)
: Container(),
Container(
color: Colors.transparent,
constraints: constraints ??
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .8),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
child: Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(bubbleRadius),
topRight: Radius.circular(bubbleRadius),
bottomLeft: Radius.circular(tail
? isSender
? bubbleRadius
: 0
: defaultBubbleRadiusAudio),
bottomRight: Radius.circular(tail
? isSender
? 0
: bubbleRadius
: defaultBubbleRadiusAudio),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isForwarded)
Padding(
padding: const EdgeInsets.fromLTRB(12, 8, 12, 0),
child: BubbleForwardedHeader(color: forwardedColor),
),
// Player row: play/pause button + waveform or slider
Row(
children: [
// Playback speed button (shown before play button when enabled)
if (showPlaybackSpeed)
GestureDetector(
onTap: onPlaybackSpeedChanged != null
? () {
final double next =
playbackSpeed >= 2.0
? 1.0
: playbackSpeed >= 1.5
? 2.0
: 1.5;
onPlaybackSpeedChanged!(next);
}
: null,
child: Container(
margin: const EdgeInsets.only(left: 8),
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
decoration: BoxDecoration(
border: Border.all(
color: textStyle.color ?? Colors.black54,
width: 1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_speedLabel(playbackSpeed),
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: textStyle.color ?? Colors.black87,
),
),
),
),
RawMaterialButton(
onPressed: onPlayPauseButtonClick,
elevation: 1.0,
fillColor: Colors.white,
padding: EdgeInsets.all(0.0),
shape: CircleBorder(),
child: !isPlaying
? Icon(Icons.play_arrow, size: 30.0)
: isLoading
? CircularProgressIndicator()
: isPause
? Icon(Icons.play_arrow, size: 30.0)
: Icon(Icons.pause, size: 30.0),
),
Expanded(
child: waveformData != null && waveformData!.isNotEmpty
? _buildWaveform(waveformData!, waveProgress, dur)
: Slider(
min: 0.0,
max: dur,
value: pos,
onChanged: onSeekChanged,
),
),
],
),
// Bottom row: audio timer + status
Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 8, 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
audioTimer(dur, pos),
style: textStyle,
),
if (showStatusArea)
BubbleStatusRow(
stateIcon: stateTick ? stateIcon : null,
isEdited: isEdited,
timestamp: timestamp,
textColor: textStyle.color ?? Colors.black87,
),
],
),
),
],
),
),
),
),
],
);
}