Add floating audio + update code version + to 1.0.6

This commit is contained in:
Fransolet Thomas 2023-03-22 15:44:22 +01:00
parent beb0277d46
commit 796cd72ef6
3 changed files with 252 additions and 10 deletions

View File

@ -18,6 +18,8 @@ import 'package:mymuseum_visitapp/client.dart';
import 'package:mymuseum_visitapp/constants.dart'; import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'audio_player_floating.dart';
class ArticlePage extends StatefulWidget { class ArticlePage extends StatefulWidget {
const ArticlePage({Key? key, required this.visitAppContextIn, required this.articleId}) : super(key: key); const ArticlePage({Key? key, required this.visitAppContextIn, required this.articleId}) : super(key: key);
@ -65,7 +67,7 @@ class _ArticlePageState extends State<ArticlePage> {
isTextSizeButton: true, isTextSizeButton: true,
), ),
body: FutureBuilder( body: FutureBuilder(
future: getArticle(appContext, appContext.clientAPI, widget.articleId), future: getArticle(appContext, appContext.clientAPI, widget.articleId, false),
builder: (context, AsyncSnapshot<dynamic> snapshot) { builder: (context, AsyncSnapshot<dynamic> snapshot) {
if(articleDTO != null && sectionDTO != null) { if(articleDTO != null && sectionDTO != null) {
if(size.height > size.width) { if(size.height > size.width) {
@ -81,8 +83,8 @@ class _ArticlePageState extends State<ArticlePage> {
if(!articleDTO!.isContentTop!) if(!articleDTO!.isContentTop!)
getContent(size, appContext), getContent(size, appContext),
if(audioResourceModel != null) /*if(audioResourceModel != null)
AudioPlayerContainer(audioBytes: audiobytes, isAuto: articleDTO!.isReadAudioAuto!), AudioPlayerContainer(audioBytes: audiobytes, isAuto: articleDTO!.isReadAudioAuto!),*/
], ],
); );
} else { } else {
@ -105,8 +107,8 @@ class _ArticlePageState extends State<ArticlePage> {
getContent(size, appContext), getContent(size, appContext),
], ],
), ),
if(audioResourceModel != null) /*if(audioResourceModel != null)
AudioPlayerContainer(audioBytes: audiobytes, isAuto: articleDTO!.isReadAudioAuto!) AudioPlayerContainer(audioBytes: audiobytes, isAuto: articleDTO!.isReadAudioAuto!)*/
], ],
), ),
); );
@ -116,7 +118,16 @@ class _ArticlePageState extends State<ArticlePage> {
} }
} }
), ),
//floatingActionButton: const ScannerBouton(isReplacement: true), floatingActionButton: FutureBuilder(
future: getArticle(appContext, appContext.clientAPI, widget.articleId, true),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
return Padding(
padding: EdgeInsets.only(right: 0, top: 0), //size.height*0.1
child: audioResourceModel != null ? AudioPlayerFloatingContainer(audioBytes: audiobytes, isAuto: articleDTO!.isReadAudioAuto!) : null,
);
}
),
floatingActionButtonLocation: FloatingActionButtonLocation.miniEndFloat, //miniEndTop
); );
} }
@ -244,7 +255,7 @@ class _ArticlePageState extends State<ArticlePage> {
} }
} }
Future<ArticleDTO?> getArticle(AppContext appContext, Client client, String articleId) async { Future<ArticleDTO?> getArticle(AppContext appContext, Client client, String articleId, bool isAudio) async {
try { try {
if(sectionDTO == null || articleDTO == null) { if(sectionDTO == null || articleDTO == null) {
bool isConfigOffline = (appContext.getContext() as VisitAppContext).configuration!.isOffline!; bool isConfigOffline = (appContext.getContext() as VisitAppContext).configuration!.isOffline!;
@ -282,7 +293,7 @@ class _ArticlePageState extends State<ArticlePage> {
if(sectionDTO!.type == SectionType.Article) { if(sectionDTO!.type == SectionType.Article) {
articleDTO = ArticleDTO.fromJson(jsonDecode(sectionDTO!.data!)); articleDTO = ArticleDTO.fromJson(jsonDecode(sectionDTO!.data!));
} }
if(articleDTO != null) { if(articleDTO != null && isAudio) {
var audioIdArticle = articleDTO!.audioIds!.where((audioId) => audioId.language == (appContext.getContext() as VisitAppContext).language); var audioIdArticle = articleDTO!.audioIds!.where((audioId) => audioId.language == (appContext.getContext() as VisitAppContext).language);
if(audioIdArticle.isNotEmpty && audioIdArticle.first.value != null) { if(audioIdArticle.isNotEmpty && audioIdArticle.first.value != null) {
if(isConfigOffline) if(isConfigOffline)
@ -319,7 +330,7 @@ class _ArticlePageState extends State<ArticlePage> {
} }
} }
if(articleDTO!.images!.isNotEmpty) { if(articleDTO!.images!.isNotEmpty && !isAudio) {
for (var image in articleDTO!.images!) { for (var image in articleDTO!.images!) {
if(image.resourceId != null) { if(image.resourceId != null) {
if(isConfigOffline) if(isConfigOffline)

View File

@ -0,0 +1,231 @@
import 'dart:typed_data';
//import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:mymuseum_visitapp/Helpers/translationHelper.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart';
import 'package:just_audio/just_audio.dart';
class AudioPlayerFloatingContainer extends StatefulWidget {
const AudioPlayerFloatingContainer({Key? key, required this.audioBytes, required this.isAuto}) : super(key: key);
final Uint8List audioBytes;
final bool isAuto;
@override
State<AudioPlayerFloatingContainer> createState() => _AudioPlayerFloatingContainerState();
}
class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContainer> {
AudioPlayer player = AudioPlayer();
late Uint8List audiobytes;
bool isplaying = false;
bool audioplayed = false;
int currentpos = 0;
int maxduration = 100;
String currentpostlabel = "00:00";
@override
void initState() {
Future.delayed(Duration.zero, () async {
audiobytes = widget.audioBytes;
/*player.onDurationChanged.listen((Duration d) { //get the duration of audio
maxduration = d.inSeconds;
setState(() {
});
});*/
//player.bufferedPositionStream
player.positionStream.listen((event) {
if(event != null) {
currentpos = event.inMilliseconds; //get the current position of playing audio
//generating the duration label
int shours = Duration(milliseconds:currentpos).inHours;
int sminutes = Duration(milliseconds:currentpos).inMinutes;
int sseconds = Duration(milliseconds:currentpos).inSeconds;
int rminutes = sminutes - (shours * 60);
int rseconds = sseconds - (sminutes * 60 + shours * 60 * 60);
String minutesToShow = rminutes < 10 ? '0$rminutes': rminutes.toString();
String secondsToShow = rseconds < 10 ? '0$rseconds': rseconds.toString();
currentpostlabel = "$minutesToShow:$secondsToShow";
setState(() {
//refresh the UI
if(currentpos > player.duration!.inMilliseconds) {
print("RESET ALL");
player.stop();
player.seek(const Duration(seconds: 0));
isplaying = false;
audioplayed = false;
currentpostlabel = "00:00";
}
});
}
});
/*player.onPositionChanged.listen((Duration p){
currentpos = p.inMilliseconds; //get the current position of playing audio
//generating the duration label
int shours = Duration(milliseconds:currentpos).inHours;
int sminutes = Duration(milliseconds:currentpos).inMinutes;
int sseconds = Duration(milliseconds:currentpos).inSeconds;
int rminutes = sminutes - (shours * 60);
int rseconds = sseconds - (sminutes * 60 + shours * 60 * 60);
String minutesToShow = rminutes < 10 ? '0$rminutes': rminutes.toString();
String secondsToShow = rseconds < 10 ? '0$rseconds': rseconds.toString();
currentpostlabel = "$minutesToShow:$secondsToShow";
setState(() {
//refresh the UI
});
});*/
if(widget.isAuto) {
//player.play(BytesSource(audiobytes));
await player.setAudioSource(LoadedSource(audiobytes));
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}
});
super.initState();
}
@override
void dispose() {
player.stop();
player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
return FloatingActionButton(
backgroundColor: kBlue1.withOpacity(0.7),
onPressed: () async {
print("TODO");
if(!isplaying && !audioplayed){
//player.play(BytesSource(audiobytes));
await player.setAudioSource(LoadedSource(audiobytes));
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else if(audioplayed && !isplaying){
//player.resume();
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else{
player.pause();
setState(() {
isplaying = false;
});
}
},
child: isplaying ? const Icon(Icons.pause) : const Icon(Icons.play_arrow),
/*Column(
children: [
//Text(currentpostlabel, style: const TextStyle(fontSize: 25)),
Wrap(
spacing: 10,
children: [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: kSecondColor, // Background color
),
onPressed: () async {
if(!isplaying && !audioplayed){
//player.play(BytesSource(audiobytes));
await player.setAudioSource(LoadedSource(audiobytes));
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else if(audioplayed && !isplaying){
//player.resume();
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else{
player.pause();
setState(() {
isplaying = false;
});
}
},
icon: Icon(isplaying?Icons.pause:Icons.play_arrow),
//label:Text(isplaying?TranslationHelper.getFromLocale("pause", appContext.getContext()):TranslationHelper.getFromLocale("play", appContext.getContext()))
),
/*ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: kSecondColor, // Background color
),
onPressed: () async {
player.stop();
player.seek(const Duration(seconds: 0));
setState(() {
isplaying = false;
audioplayed = false;
currentpostlabel = "00:00";
});
},
icon: const Icon(Icons.stop),
//label: Text(TranslationHelper.getFromLocale("stop", appContext.getContext()))
),*/
],
)
],
),*/
);
}
}
// Feed your own stream of bytes into the player
class LoadedSource extends StreamAudioSource {
final List<int> bytes;
LoadedSource(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/mpeg',
);
}
}

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.5+13 version: 1.0.6+14
environment: environment:
sdk: ">=2.16.2 <3.0.0" sdk: ">=2.16.2 <3.0.0"