didvan-app/lib/views/ai/widgets/voice_message_view.dart

295 lines
9.2 KiB
Dart

// ignore_for_file: implementation_imports, unused_element, empty_catches
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:flutter/material.dart';
import 'package:voice_message_package/src/helpers/play_status.dart';
import 'package:voice_message_package/src/helpers/utils.dart';
import 'package:voice_message_package/src/voice_controller.dart';
import 'package:voice_message_package/src/widgets/noises.dart';
import 'package:voice_message_package/src/widgets/play_pause_button.dart';
/// A widget that displays a voice message view with play/pause functionality.
///
/// The [VoiceMessageView] widget is used to display a voice message with customizable appearance and behavior.
/// It provides a play/pause button, a progress slider, and a counter for the remaining time.
/// The appearance of the widget can be customized using various properties such as background color, slider color, and text styles.
///
class MyVoiceMessageView extends StatelessWidget {
const MyVoiceMessageView(
{Key? key,
required this.controller,
this.backgroundColor = Colors.transparent,
this.activeSliderColor = Colors.red,
this.notActiveSliderColor,
this.circlesColor = Colors.red,
this.innerPadding = 12,
this.cornerRadius = 20,
// this.playerWidth = 170,
this.size = 38,
this.refreshIcon = const Icon(
Icons.refresh,
color: Colors.white,
),
this.pauseIcon = const Icon(
Icons.pause_rounded,
color: Colors.white,
),
this.playIcon = const Icon(
Icons.play_arrow_rounded,
color: Colors.white,
),
this.stopDownloadingIcon = const Icon(
Icons.close,
color: Colors.white,
),
this.playPauseButtonDecoration,
this.circlesTextStyle = const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
this.counterTextStyle = const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
),
this.playPauseButtonLoadingColor = Colors.white,
this.trashClick,
this.showSpeed = false})
: super(key: key);
/// The controller for the voice message view.
final VoiceController controller;
/// The background color of the voice message view.
final Color backgroundColor;
///
final Color circlesColor;
/// The color of the active slider.
final Color activeSliderColor;
/// The color of the not active slider.
final Color? notActiveSliderColor;
/// The text style of the circles.
final TextStyle circlesTextStyle;
/// The text style of the counter.
final TextStyle counterTextStyle;
/// The padding between the inner content and the outer container.
final double innerPadding;
/// The corner radius of the outer container.
final double cornerRadius;
/// The size of the play/pause button.
final double size;
/// The refresh icon of the play/pause button.
final Widget refreshIcon;
/// The pause icon of the play/pause button.
final Widget pauseIcon;
/// The play icon of the play/pause button.
final Widget playIcon;
/// The stop downloading icon of the play/pause button.
final Widget stopDownloadingIcon;
/// The play Decoration of the play/pause button.
final Decoration? playPauseButtonDecoration;
/// The loading Color of the play/pause button.
final Color playPauseButtonLoadingColor;
final Function()? trashClick;
final bool showSpeed;
@override
/// Build voice message view.
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final color = circlesColor;
final newTHeme = theme.copyWith(
sliderTheme: SliderThemeData(
trackShape: CustomTrackShape(),
thumbShape: SliderComponentShape.noThumb,
minThumbSeparation: 0,
),
splashColor: Colors.transparent,
);
return Directionality(
textDirection: TextDirection.ltr,
child: Container(
width: 160 + (controller.noiseCount * .72.w()),
padding: EdgeInsets.all(innerPadding),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(cornerRadius),
),
child: ValueListenableBuilder(
/// update ui when change play status
valueListenable: controller.updater,
builder: (context, value, child) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(width: 12),
/// play pause button
PlayPauseButton(
controller: controller,
color: color,
loadingColor: playPauseButtonLoadingColor,
size: size,
refreshIcon: refreshIcon,
pauseIcon: pauseIcon,
playIcon: playIcon,
stopDownloadingIcon: stopDownloadingIcon,
buttonDecoration: playPauseButtonDecoration,
),
const SizedBox(width: 12),
///
/// slider & noises
Expanded(
child: _noises(newTHeme),
),
const SizedBox(width: 12),
Text(controller.remindingTime, style: counterTextStyle),
const SizedBox(width: 12),
///
/// speed button
if (showSpeed) _changeSpeedButton(color),
///
if (trashClick != null)
MessageBarBtn(
enable: true,
icon: DidvanIcons.trash_solid,
color: Theme.of(context).colorScheme.error,
click: () async {
trashClick?.call();
}),
const SizedBox(width: 12),
],
);
},
),
),
);
}
SizedBox _noises(ThemeData newTHeme) => SizedBox(
child: Stack(
alignment: Alignment.center,
children: [
/// noises
Noises(
rList: controller.randoms!,
activeSliderColor: activeSliderColor,
),
/// slider
AnimatedBuilder(
animation: CurvedAnimation(
parent: controller.animController,
curve: Curves.ease,
),
builder: (BuildContext context, Widget? child) {
return Positioned(
left: controller.animController.value,
child: Container(
width: controller.noiseWidth,
height: 6.w(),
color: notActiveSliderColor ?? backgroundColor,
),
);
},
),
Opacity(
opacity: 0,
child: Container(
width: controller.noiseWidth,
color: Colors.transparent.withOpacity(1),
child: Theme(
data: newTHeme,
child: Slider(
value: controller.currentMillSeconds,
max: controller.maxMillSeconds,
onChangeStart: controller.onChangeSliderStart,
onChanged: controller.onChanging,
onChangeEnd: (value) {
controller.onSeek(
Duration(milliseconds: value.toInt()),
);
controller.play();
},
),
),
),
),
],
),
);
Transform _changeSpeedButton(Color color) => Transform.translate(
offset: const Offset(0, -7),
child: GestureDetector(
onTap: () {
controller.changeSpeed();
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 2),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(4),
),
child: Text(
controller.speed.playSpeedStr,
style: circlesTextStyle,
),
),
),
);
}
///
/// A custom track shape for a slider that is rounded rectangular in shape.
/// Extends the [RoundedRectSliderTrackShape] class.
class CustomTrackShape extends RoundedRectSliderTrackShape {
@override
/// Returns the preferred rectangle for the voice message view.
///
/// The preferred rectangle is calculated based on the current state and layout
/// of the voice message view. It represents the area where the view should be
/// displayed on the screen.
///
/// Returns a [Rect] object representing the preferred rectangle.
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
const double trackHeight = 10;
final double trackLeft = offset.dx,
trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}