|
|
|
@ -61,11 +61,17 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
required SingleUserChatModel data,
|
|
|
|
|
}) async {
|
|
|
|
|
if (data.voice != null && data.voice!.existsSync()) {
|
|
|
|
|
await data.voiceController!.setFilePath(data!.voice!.path);
|
|
|
|
|
await data.voiceController!.setLoopMode(LoopMode.off);
|
|
|
|
|
Duration? duration = await data.voiceController!.load();
|
|
|
|
|
await data.voiceController!.seek(duration);
|
|
|
|
|
await data.voiceController!.play();
|
|
|
|
|
if (Platform.isIOS) {
|
|
|
|
|
Duration? duration = await data.voiceController!.setAudioSource(MyCustomStream(data.voice!.readAsBytesSync()));
|
|
|
|
|
await data.voiceController!.seek(duration);
|
|
|
|
|
data.voiceController!.play();
|
|
|
|
|
} else {
|
|
|
|
|
await data.voiceController!.setFilePath(data!.voice!.path);
|
|
|
|
|
Duration? duration = await data.voiceController!.load();
|
|
|
|
|
await data.voiceController!.seek(duration);
|
|
|
|
|
await data.voiceController!.play();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Utils.showLoading(context);
|
|
|
|
|
Uint8List encodedString = await ChatApiClient().downloadURL(fileName: data.contant!, fileTypeDescription: provider.getFileTypeDescription(data.fileTypeResponse!.fileTypeName ?? ""));
|
|
|
|
@ -74,13 +80,19 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
File file = File(path!);
|
|
|
|
|
await file.readAsBytes();
|
|
|
|
|
data.voice = file;
|
|
|
|
|
Duration? duration = await data.voiceController!.setFilePath(file.path);
|
|
|
|
|
await data.voiceController!.setLoopMode(LoopMode.off);
|
|
|
|
|
await data.voiceController!.seek(duration);
|
|
|
|
|
await data.voiceController!.setVolume(1.0);
|
|
|
|
|
await data.voiceController!.load();
|
|
|
|
|
Utils.hideLoading(context);
|
|
|
|
|
await data.voiceController!.play();
|
|
|
|
|
if (Platform.isIOS) {
|
|
|
|
|
Duration? duration = await data.voiceController!.setAudioSource(MyCustomStream(encodedString));
|
|
|
|
|
await data.voiceController!.seek(duration);
|
|
|
|
|
data.voiceController!.play();
|
|
|
|
|
} else {
|
|
|
|
|
Duration? duration = await data.voiceController!.setFilePath(file.path);
|
|
|
|
|
await data.voiceController!.setLoopMode(LoopMode.off);
|
|
|
|
|
await data.voiceController!.seek(duration);
|
|
|
|
|
await data.voiceController!.setVolume(1.0);
|
|
|
|
|
await data.voiceController!.load();
|
|
|
|
|
Utils.hideLoading(context);
|
|
|
|
|
await data.voiceController!.play();
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
Utils.hideLoading(context);
|
|
|
|
|
Utils.showToast(e.toString());
|
|
|
|
@ -114,7 +126,7 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
Widget currentUser(BuildContext context) {
|
|
|
|
|
return Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
if (isReplied)
|
|
|
|
|
ClipRRect(
|
|
|
|
|
borderRadius: BorderRadius.circular(5.0),
|
|
|
|
@ -200,7 +212,7 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
).paddingOnly(top: 11, left: 13, right: 13, bottom: 5).objectContainerView(disablePadding: true).paddingOnly(left: MediaQuery.of(context).size.width * 0.3);
|
|
|
|
|
).paddingOnly(top: 11, left: 13, right: 13, bottom: 5).objectContainerView(disablePadding: true, radius: 10).paddingOnly(left: MediaQuery.of(context).size.width * 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget receiptUser(BuildContext context) {
|
|
|
|
@ -340,6 +352,7 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
Widget currentWaveBubble(BuildContext context, SingleUserChatModel data) {
|
|
|
|
|
return Container(
|
|
|
|
|
margin: const EdgeInsets.all(0),
|
|
|
|
|
constraints: const BoxConstraints(),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
border: Border(
|
|
|
|
|
left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white),
|
|
|
|
@ -411,7 +424,7 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
child: const CircularProgressIndicator(),
|
|
|
|
|
);
|
|
|
|
|
} else if (playing != true) {
|
|
|
|
|
return Icon(
|
|
|
|
|
return const Icon(
|
|
|
|
|
Icons.play_arrow,
|
|
|
|
|
size: 30,
|
|
|
|
|
color: MyColors.lightGreenColor,
|
|
|
|
@ -419,7 +432,7 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
playVoice(context, data: modelData);
|
|
|
|
|
});
|
|
|
|
|
} else if (processingState != ProcessingState.completed) {
|
|
|
|
|
return Icon(
|
|
|
|
|
return const Icon(
|
|
|
|
|
Icons.pause,
|
|
|
|
|
size: 30,
|
|
|
|
|
color: MyColors.lightGreenColor,
|
|
|
|
@ -427,7 +440,7 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
pausePlaying(context, data: modelData);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
return Icon(
|
|
|
|
|
return const Icon(
|
|
|
|
|
Icons.replay,
|
|
|
|
|
size: 30,
|
|
|
|
|
color: MyColors.lightGreenColor,
|
|
|
|
@ -439,3 +452,23 @@ class ChatBubble extends StatelessWidget {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Feed your own stream of bytes into the player
|
|
|
|
|
class MyCustomStream extends StreamAudioSource {
|
|
|
|
|
final Uint8List bytes;
|
|
|
|
|
|
|
|
|
|
MyCustomStream(this.bytes);
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<StreamAudioResponse> request([int? start, int? end]) async {
|
|
|
|
|
start ??= 0;
|
|
|
|
|
end ??= bytes.length;
|
|
|
|
|
return StreamAudioResponse(
|
|
|
|
|
sourceLength: bytes.length,
|
|
|
|
|
contentLength: end - start,
|
|
|
|
|
offset: start,
|
|
|
|
|
stream: Stream.value(bytes.sublist(start, end)),
|
|
|
|
|
contentType: 'audio/aac',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|