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

318 lines
13 KiB
Dart

// ignore_for_file: implementation_imports, library_private_types_in_public_api
import 'dart:math';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/utils/media.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
class AudioWave extends StatefulWidget {
final String file;
final double loadingPaddingSize;
const AudioWave({Key? key, required this.file, this.loadingPaddingSize = 0})
: super(key: key);
@override
_AudioWaveState createState() => _AudioWaveState();
}
class _AudioWaveState extends State<AudioWave> {
final int itemCount = 35;
final AudioPlayer audioPlayer = AudioPlayer();
final ValueNotifier<List<double>> randoms = ValueNotifier([]);
final ValueNotifier<List<double>> randomsDisable = ValueNotifier([]);
Duration totalDuration = Duration.zero;
double currentPosition = 0;
bool loading = true;
bool faile = false;
@override
void initState() {
super.initState();
try {
init();
listeners();
} catch (e) {
if (kDebugMode) {
print('Error occurred: $e');
}
rethrow;
}
}
void setRandoms() {
for (var i = 0; i < itemCount; i++) {
randoms.value.add(0);
randomsDisable.value.add(5.74.w() * Random().nextDouble() + .26.w());
}
}
Future<void> init() async {
try {
final path = widget.file;
if (widget.file.startsWith('/uploads')) {
final audioSource = LockCachingAudioSource(Uri.parse(
'${RequestHelper.baseUrl + path}?accessToken=${RequestService.token}'));
await audioSource.request();
totalDuration =
await audioPlayer.setAudioSource(audioSource) ?? Duration.zero;
} else {
totalDuration = await audioPlayer.setFilePath(path) ?? Duration.zero;
}
setRandoms();
setState(() {
loading = false;
});
} catch (e) {
setState(() {
faile = true;
loading = false;
});
if (kDebugMode) {
print('Error occurred: $e');
}
}
}
Future<void> listeners() async {
audioPlayer.positionStream.listen((position) async {
if (randomsDisable.value.isEmpty) return;
try {
for (var i = 0; i < itemCount; i++) {
if (i < randomsDisable.value.length &&
i <
((position.inMilliseconds * 40) /
totalDuration.inMilliseconds)) {
final ran = randomsDisable.value[i];
randoms.value[i] = ran;
} else {
randoms.value[i] = 0;
}
}
} catch (e) {
e.printError(info: 'listener Error');
}
if (position.inMilliseconds >= totalDuration.inMilliseconds) {
audioPlayer.stop();
audioPlayer.seek(Duration.zero);
}
});
}
@override
void dispose() {
audioPlayer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 46,
child: loading
? Padding(
padding:
EdgeInsets.symmetric(vertical: widget.loadingPaddingSize),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
5,
(index) => SpinKitWave(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4),
size: 32,
itemCount: 10,
))),
)
: Directionality(
textDirection: TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder<PlayerState>(
stream: audioPlayer.playerStateStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
return MessageBarBtn(
enable: true,
icon: faile
? DidvanIcons.refresh_solid
: snapshot.data!.playing
? DidvanIcons.pause_solid
: DidvanIcons.play_solid,
click: () async {
if (faile) {
randoms.value.clear();
randomsDisable.value.clear();
setState(() {
loading = true;
faile = false;
});
init();
return;
}
if (snapshot.data!.playing) {
await audioPlayer.pause();
} else {
await audioPlayer.play();
}
},
);
}),
faile
? const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: DidvanText(
'خطا در بارگزاری فایل صوتی',
fontSize: 12,
),
)
: StreamBuilder<Duration>(
stream: audioPlayer.positionStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
currentPosition =
snapshot.data!.inMilliseconds.toDouble();
return Expanded(
child: Row(
children: [
ValueListenableBuilder(
valueListenable: randoms,
builder: (context, value, child) {
return Expanded(
child: Stack(
alignment: Alignment.center,
children: [
noise(values: randoms.value),
noise(
values: randomsDisable.value,
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4)),
if (totalDuration != Duration.zero)
Opacity(
opacity: 0,
child: Container(
width: 50.5.w(),
color: Colors.transparent
.withOpacity(1),
child: Theme(
data: Theme.of(context)
.copyWith(
sliderTheme:
SliderThemeData(
thumbShape:
SliderComponentShape
.noThumb,
minThumbSeparation: 0,
),
splashColor:
Colors.transparent,
),
child: Slider(
value: currentPosition,
max: totalDuration
.inMilliseconds
.toDouble() +
const Duration(
milliseconds:
10)
.inMilliseconds
.toDouble(),
onChangeStart: (value) {
// audioPlayer.pause();
},
onChanged: (value) {
for (var i = 0;
i < itemCount;
i++) {
if (i <
((value * 40) /
totalDuration
.inMilliseconds)) {
final ran =
randomsDisable
.value[i];
randoms.value[i] =
ran;
} else {
randoms.value[i] = 0;
}
}
setState(() {
currentPosition = value;
});
},
onChangeEnd: (value) {
audioPlayer.seek(Duration(
milliseconds:
value.round()));
audioPlayer.play();
},
),
),
),
),
],
));
},
),
DidvanText(
DateTimeUtils.normalizeTimeDuration(
snapshot.data! == Duration.zero
? totalDuration
: snapshot.data!)),
],
),
);
},
)
],
),
),
);
}
Row noise({required final List<double> values, final Color? color}) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: values
.map(
(e) => Container(
margin: EdgeInsets.symmetric(horizontal: .2.w()),
width: .56.w(),
height: e,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(1000),
color: color ?? Theme.of(context).colorScheme.primary,
),
),
)
.toList(),
);
}
}