Puzzle working + slider wip + misc
This commit is contained in:
parent
bddde86974
commit
7aca0638ce
265
lib/Components/audio_player.dart
Normal file
265
lib/Components/audio_player.dart
Normal file
@ -0,0 +1,265 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:just_audio_cache/just_audio_cache.dart';
|
||||
|
||||
|
||||
class AudioPlayerFloatingContainer extends StatefulWidget {
|
||||
const AudioPlayerFloatingContainer({Key? key, required this.file, required this.audioBytes, required this.resourceURl, required this.isAuto}) : super(key: key);
|
||||
|
||||
final File? file;
|
||||
final Uint8List? audioBytes;
|
||||
final String resourceURl;
|
||||
final bool isAuto;
|
||||
|
||||
@override
|
||||
State<AudioPlayerFloatingContainer> createState() => _AudioPlayerFloatingContainerState();
|
||||
}
|
||||
|
||||
class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContainer> {
|
||||
AudioPlayer player = AudioPlayer();
|
||||
Uint8List? audiobytes = null;
|
||||
bool isplaying = false;
|
||||
bool audioplayed = false;
|
||||
int currentpos = 0;
|
||||
int maxduration = 100;
|
||||
Duration? durationAudio;
|
||||
String currentpostlabel = "00:00";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
//print("IN INITSTATE AUDDDIOOOO");
|
||||
Future.delayed(Duration.zero, () async {
|
||||
if(widget.audioBytes != null) {
|
||||
audiobytes = widget.audioBytes!;
|
||||
}
|
||||
|
||||
if(widget.file != null) {
|
||||
audiobytes = await fileToUint8List(widget.file!);
|
||||
}
|
||||
|
||||
player.durationStream.listen((Duration? d) { //get the duration of audio
|
||||
if(d != null) {
|
||||
maxduration = d.inSeconds;
|
||||
durationAudio = d;
|
||||
}
|
||||
});
|
||||
|
||||
//player.bufferedPositionStream
|
||||
|
||||
player.positionStream.listen((event) {
|
||||
if(durationAudio != null) {
|
||||
|
||||
currentpos = event.inMilliseconds; //get the current position of playing audio
|
||||
|
||||
//generating the duration label
|
||||
int shours = Duration(milliseconds:durationAudio!.inMilliseconds - currentpos).inHours;
|
||||
int sminutes = Duration(milliseconds:durationAudio!.inMilliseconds - currentpos).inMinutes;
|
||||
int sseconds = Duration(milliseconds:durationAudio!.inMilliseconds - 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(audiobytes != null) {
|
||||
print("GOT AUDIOBYYYTES - LOCALLY SOSO");
|
||||
await player.setAudioSource(LoadedSource(audiobytes!));
|
||||
} else {
|
||||
print("GET SOUND BY URL");
|
||||
await player.dynamicSet(url: widget.resourceURl);
|
||||
}
|
||||
|
||||
if(widget.isAuto) {
|
||||
//player.play(BytesSource(audiobytes));
|
||||
//
|
||||
player.play();
|
||||
setState(() {
|
||||
isplaying = true;
|
||||
audioplayed = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
player.stop();
|
||||
player.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<Uint8List> fileToUint8List(File file) async {
|
||||
List<int> bytes = await file.readAsBytes();
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
|
||||
return FloatingActionButton(
|
||||
backgroundColor: Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)).withValues(alpha: 0.7),
|
||||
onPressed: () async {
|
||||
if(!isplaying && !audioplayed){
|
||||
//player.play(BytesSource(audiobytes));
|
||||
//await player.setUrl(widget.resourceURl);
|
||||
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 ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.pause),
|
||||
Text(currentpostlabel),
|
||||
],
|
||||
) : audioplayed ? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.play_arrow),
|
||||
Text(currentpostlabel),
|
||||
],
|
||||
): 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',
|
||||
);
|
||||
}
|
||||
}
|
||||
126
lib/Components/cached_custom_resource.dart
Normal file
126
lib/Components/cached_custom_resource.dart
Normal file
@ -0,0 +1,126 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:mymuseum_visitapp/Components/audio_player.dart';
|
||||
import 'package:mymuseum_visitapp/Components/video_viewer.dart';
|
||||
import 'package:mymuseum_visitapp/Components/video_viewer_youtube.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
class CachedCustomResource extends StatelessWidget {
|
||||
final ResourceDTO resourceDTO;
|
||||
final bool isAuto;
|
||||
final bool webView;
|
||||
final BoxFit fit;
|
||||
|
||||
CachedCustomResource({
|
||||
required this.resourceDTO,
|
||||
required this.isAuto,
|
||||
required this.webView,
|
||||
this.fit = BoxFit.cover,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
Size size = MediaQuery.of(context).size;
|
||||
|
||||
Color primaryColor = Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16));
|
||||
|
||||
if(resourceDTO.type == ResourceType.ImageUrl || resourceDTO.type == ResourceType.VideoUrl)
|
||||
{
|
||||
// Image Url or Video Url don't care, just get resource
|
||||
if(resourceDTO.type == ResourceType.ImageUrl) {
|
||||
return CachedNetworkImage(
|
||||
imageUrl: resourceDTO.url!,
|
||||
fit: BoxFit.fill,
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
CircularProgressIndicator(value: downloadProgress.progress, color: primaryColor),
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
);
|
||||
} else {
|
||||
if(resourceDTO.url == null) {
|
||||
return const Center(child: Text("Error loading video"));
|
||||
} else {
|
||||
return VideoViewerYoutube(videoUrl: resourceDTO.url!, isAuto: isAuto, webView: webView);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check if exist on local storage, if no, just show it via url
|
||||
print("Check local storage in cached custom resource");
|
||||
return FutureBuilder<File?>(
|
||||
future: _checkIfLocalResourceExists(visitAppContext),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
// Loader ou indicateur de chargement pendant la vérification
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasError || snapshot.data == null) {
|
||||
// Si la ressource locale n'existe pas ou s'il y a une erreur
|
||||
|
||||
switch(resourceDTO.type) {
|
||||
case ResourceType.Image :
|
||||
return CachedNetworkImage(
|
||||
imageUrl: resourceDTO.url!,
|
||||
fit: fit,
|
||||
placeholder: (context, url) => const CircularProgressIndicator(),
|
||||
errorWidget: (context, url, error) => const Icon(Icons.error),
|
||||
);
|
||||
case ResourceType.Video :
|
||||
return VideoViewer(file: null, videoUrl: resourceDTO.url!);
|
||||
case ResourceType.Audio :
|
||||
return AudioPlayerFloatingContainer(file: null, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto);
|
||||
default:
|
||||
return const Text("Not supported type");
|
||||
}
|
||||
} else {
|
||||
|
||||
switch(resourceDTO.type) {
|
||||
case ResourceType.Image :
|
||||
return Image.file(
|
||||
snapshot.data!,
|
||||
fit: fit,
|
||||
);
|
||||
case ResourceType.Video :
|
||||
return VideoViewer(file: snapshot.data!, videoUrl: resourceDTO.url!);
|
||||
case ResourceType.Audio :
|
||||
return AudioPlayerFloatingContainer(file: snapshot.data!, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto);
|
||||
default:
|
||||
return const Text("Not supported type");
|
||||
}
|
||||
// Utilisation de l'image locale
|
||||
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<File?> _checkIfLocalResourceExists(VisitAppContext visitAppContext) async {
|
||||
try {
|
||||
Directory? appDocumentsDirectory = Platform.isIOS ? await getApplicationDocumentsDirectory() : await getDownloadsDirectory();
|
||||
String localPath = appDocumentsDirectory!.path;
|
||||
Directory configurationDirectory = Directory('$localPath/${visitAppContext.configuration!.id}');
|
||||
List<FileSystemEntity> fileList = configurationDirectory.listSync();
|
||||
|
||||
if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(resourceDTO.id!))) {
|
||||
File file = File(fileList.firstWhere((fileL) => fileL.uri.pathSegments.last.contains(resourceDTO.id!)).path);
|
||||
return file;
|
||||
}
|
||||
} catch(e) {
|
||||
print("ERROR _checkIfLocalResourceExists CachedCustomResource");
|
||||
print(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String> get localPath async {
|
||||
Directory? appDocumentsDirectory = Platform.isIOS ? await getApplicationDocumentsDirectory() : await getDownloadsDirectory();
|
||||
return appDocumentsDirectory!.path;
|
||||
}
|
||||
}
|
||||
92
lib/Components/show_element_for_resource.dart
Normal file
92
lib/Components/show_element_for_resource.dart
Normal file
@ -0,0 +1,92 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:mymuseum_visitapp/Components/audio_player.dart';
|
||||
import 'package:mymuseum_visitapp/Components/video_viewer.dart';
|
||||
import 'package:mymuseum_visitapp/Components/video_viewer_youtube.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
|
||||
import 'cached_custom_resource.dart';
|
||||
|
||||
showElementForResource(ResourceDTO resourceDTO, AppContext appContext, bool isAuto, bool webView) {
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
Color primaryColor = Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16));
|
||||
|
||||
return CachedCustomResource(resourceDTO: resourceDTO, isAuto: isAuto, webView: webView);
|
||||
|
||||
switch(resourceDTO.type) {
|
||||
case ResourceType.Image:
|
||||
case ResourceType.ImageUrl:
|
||||
return CachedNetworkImage(
|
||||
imageUrl: resourceDTO.url!,
|
||||
fit: BoxFit.fill,
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
CircularProgressIndicator(value: downloadProgress.progress, color: primaryColor),
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
);
|
||||
/*return Image.network(
|
||||
resourceDTO.url!,
|
||||
fit:BoxFit.fill,
|
||||
loadingBuilder: (BuildContext context, Widget child,
|
||||
ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child;
|
||||
}
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: primaryColor,
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);*/
|
||||
case ResourceType.Audio:
|
||||
return AudioPlayerFloatingContainer(file: null, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto);
|
||||
/*return FutureBuilder(
|
||||
future: getAudio(resourceDTO.url, appContext),
|
||||
builder: (context, AsyncSnapshot<dynamic> snapshot) {
|
||||
Size size = MediaQuery.of(context).size;
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
var audioBytes;
|
||||
if(snapshot.data != null) {
|
||||
print("snapshot.data");
|
||||
print(snapshot.data);
|
||||
audioBytes = snapshot.data;
|
||||
//this.player.playBytes(audiobytes);
|
||||
}
|
||||
return AudioPlayerFloatingContainer(audioBytes: audioBytes, resourceURl: resourceDTO.url, isAuto: true);
|
||||
} else if (snapshot.connectionState == ConnectionState.none) {
|
||||
return Text("No data");
|
||||
} else {
|
||||
return Center(
|
||||
child: Container(
|
||||
//height: size.height * 0.2,
|
||||
width: size.width * 0.2,
|
||||
child: LoadingCommon()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
);*/
|
||||
case ResourceType.Video:
|
||||
if(resourceDTO.url == null) {
|
||||
return Center(child: Text("Error loading video"));
|
||||
} else {
|
||||
return VideoViewer(file: null, videoUrl: resourceDTO.url!);
|
||||
}
|
||||
case ResourceType.VideoUrl:
|
||||
if(resourceDTO.url == null) {
|
||||
return Center(child: Text("Error loading video"));
|
||||
} else {
|
||||
return VideoViewerYoutube(videoUrl: resourceDTO.url!, isAuto: isAuto, webView: webView);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
lib/Components/video_viewer.dart
Normal file
94
lib/Components/video_viewer.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'dart:io';
|
||||
|
||||
//import 'package:cached_video_player/cached_video_player.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mymuseum_visitapp/Components/loading_common.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import '../../constants.dart';
|
||||
|
||||
class VideoViewer extends StatefulWidget {
|
||||
final String videoUrl;
|
||||
final File? file;
|
||||
VideoViewer({required this.videoUrl, required this.file});
|
||||
|
||||
@override
|
||||
_VideoViewer createState() => _VideoViewer();
|
||||
}
|
||||
|
||||
class _VideoViewer extends State<VideoViewer> {
|
||||
late VideoPlayerController _controller; // Cached
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if(widget.file != null) {
|
||||
_controller = VideoPlayerController.file(widget.file!) // Uri.parse() // Cached
|
||||
..initialize().then((_) {
|
||||
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
|
||||
setState(() {});
|
||||
});
|
||||
} else {
|
||||
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)) // Uri.parse()
|
||||
..initialize().then((_) {
|
||||
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_controller.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if(_controller.value.isInitialized) {
|
||||
if(_controller.value.isPlaying) {
|
||||
_controller.pause();
|
||||
} else {
|
||||
_controller.play();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
child: _controller.value.isInitialized
|
||||
? AspectRatio(
|
||||
aspectRatio: _controller.value.aspectRatio,
|
||||
child: VideoPlayer(_controller),
|
||||
)
|
||||
: Center(
|
||||
child: Container(
|
||||
child: LoadingCommon()
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
if(!_controller.value.isPlaying && _controller.value.isInitialized)
|
||||
Center(
|
||||
child: FloatingActionButton(
|
||||
backgroundColor: kMainColor.withValues(alpha: 0.8),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_controller.value.isPlaying
|
||||
? _controller.pause()
|
||||
: _controller.play();
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
color: Colors.white),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
99
lib/Components/video_viewer_youtube.dart
Normal file
99
lib/Components/video_viewer_youtube.dart
Normal file
@ -0,0 +1,99 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:mymuseum_visitapp/constants.dart';
|
||||
import 'package:youtube_player_iframe/youtube_player_iframe.dart' as iframe;
|
||||
//import 'package:youtube_player_flutter/youtube_player_flutter.dart';
|
||||
|
||||
class VideoViewerYoutube extends StatefulWidget {
|
||||
final String videoUrl;
|
||||
final bool isAuto;
|
||||
final bool webView;
|
||||
VideoViewerYoutube({required this.videoUrl, required this.isAuto, this.webView = false});
|
||||
|
||||
@override
|
||||
_VideoViewerYoutube createState() => _VideoViewerYoutube();
|
||||
}
|
||||
|
||||
class _VideoViewerYoutube extends State<VideoViewerYoutube> {
|
||||
iframe.YoutubePlayer? _videoViewWeb;
|
||||
//YoutubePlayer? _videoView;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
String? videoId;
|
||||
if (widget.videoUrl.isNotEmpty ) {
|
||||
//videoId = YoutubePlayer.convertUrlToId(widget.videoUrl);
|
||||
|
||||
if (true) {
|
||||
final _controllerWeb = iframe.YoutubePlayerController(
|
||||
params: iframe.YoutubePlayerParams(
|
||||
mute: false,
|
||||
showControls: false,
|
||||
showFullscreenButton: false,
|
||||
loop: false,
|
||||
showVideoAnnotations: false,
|
||||
strictRelatedVideos: false,
|
||||
enableKeyboard: false,
|
||||
enableCaption: false,
|
||||
pointerEvents: iframe.PointerEvents.auto
|
||||
),
|
||||
);
|
||||
|
||||
_controllerWeb.loadVideo(widget.videoUrl);
|
||||
if(!widget.isAuto) {
|
||||
_controllerWeb.stopVideo();
|
||||
}
|
||||
|
||||
_videoViewWeb = iframe.YoutubePlayer(
|
||||
controller: _controllerWeb,
|
||||
//showVideoProgressIndicator: false,
|
||||
/*progressIndicatorColor: Colors.amber,
|
||||
progressColors: ProgressBarColors(
|
||||
playedColor: Colors.amber,
|
||||
handleColor: Colors.amberAccent,
|
||||
),*/
|
||||
);
|
||||
} else /*{
|
||||
// Cause memory issue on tablet
|
||||
videoId = YoutubePlayer.convertUrlToId(widget.videoUrl);
|
||||
YoutubePlayerController _controller = YoutubePlayerController(
|
||||
initialVideoId: videoId!,
|
||||
flags: YoutubePlayerFlags(
|
||||
autoPlay: widget.isAuto,
|
||||
controlsVisibleAtStart: false,
|
||||
loop: true,
|
||||
hideControls: false,
|
||||
hideThumbnail: false,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
_videoView = YoutubePlayer(
|
||||
controller: _controller,
|
||||
//showVideoProgressIndicator: false,
|
||||
progressIndicatorColor: Colors.amber,
|
||||
progressColors: ProgressBarColors(
|
||||
playedColor: Colors.amber,
|
||||
handleColor: Colors.amberAccent,
|
||||
),
|
||||
);
|
||||
}*/
|
||||
super.initState();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
//_videoView = null;
|
||||
_videoViewWeb = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.videoUrl.isNotEmpty ?
|
||||
_videoViewWeb!: //(widget.webView ? _videoViewWeb! : _videoView!)
|
||||
const Center(child: Text("La vidéo ne peut pas être affichée, l'url est incorrecte", style: TextStyle(fontSize: kNoneInfoOrIncorrect)));
|
||||
}
|
||||
35
lib/Helpers/ImageCustomProvider.dart
Normal file
35
lib/Helpers/ImageCustomProvider.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
|
||||
class ImageCustomProvider {
|
||||
static ImageProvider<Object> getImageProvider(AppContext appContext, String? imageId, String imageSource) {
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
try {
|
||||
if(appContext.getContext().localPath != null && visitAppContext.configuration != null) {
|
||||
Directory configurationDirectory = Directory('${visitAppContext.localPath!}/${visitAppContext.configuration!.id!}');
|
||||
List<FileSystemEntity> fileList = configurationDirectory.listSync();
|
||||
|
||||
if(imageId != null && fileList.any((fileL) => fileL.uri.pathSegments.last.contains(imageId))) {
|
||||
File file = File(fileList.firstWhere((fileL) => fileL.uri.pathSegments.last.contains(imageId)).path);
|
||||
print("FILE EXISTT");
|
||||
return FileImage(file);
|
||||
}
|
||||
|
||||
}
|
||||
} catch(e) {
|
||||
print("Error getImageProvider");
|
||||
print(e.toString());
|
||||
}
|
||||
|
||||
|
||||
// If localpath not found or file missing
|
||||
print("MISSINGG FILE");
|
||||
print(imageId);
|
||||
return CachedNetworkImageProvider(imageSource);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,8 @@ class VisitAppContext with ChangeNotifier {
|
||||
bool isScanBeaconAlreadyAllowed = false;
|
||||
bool isMaximizeTextSize = false;
|
||||
|
||||
Size? puzzleSize;
|
||||
|
||||
List<ResourceModel> audiosNotWorking = [];
|
||||
|
||||
bool? isAdmin = false;
|
||||
|
||||
68
lib/Screens/Sections/Puzzle/correct_overlay.dart
Normal file
68
lib/Screens/Sections/Puzzle/correct_overlay.dart
Normal file
@ -0,0 +1,68 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CorrectOverlay extends StatefulWidget {
|
||||
final bool _isCorrect;
|
||||
final VoidCallback _onTap;
|
||||
|
||||
CorrectOverlay(this._isCorrect, this._onTap);
|
||||
|
||||
@override
|
||||
State createState() => new CorrectOverlayState();
|
||||
}
|
||||
|
||||
class CorrectOverlayState extends State<CorrectOverlay>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late Animation<double> _iconAnimation;
|
||||
late AnimationController _iconAnimationController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_iconAnimationController = new AnimationController(
|
||||
duration: new Duration(seconds: 2), vsync: this);
|
||||
_iconAnimation = new CurvedAnimation(
|
||||
parent: _iconAnimationController, curve: Curves.elasticOut);
|
||||
_iconAnimation.addListener(() => this.setState(() {}));
|
||||
_iconAnimationController.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_iconAnimationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Container(
|
||||
color: Colors.black54,
|
||||
child: new InkWell(
|
||||
onTap: () => widget._onTap(),
|
||||
child: new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
new Container(
|
||||
decoration: new BoxDecoration(
|
||||
color: Colors.blueAccent, shape: BoxShape.circle),
|
||||
child: new Transform.rotate(
|
||||
angle: _iconAnimation.value * 2 * math.pi,
|
||||
child: new Icon(
|
||||
widget._isCorrect == true ? Icons.done : Icons.clear,
|
||||
size: _iconAnimation.value * 80.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
new Padding(
|
||||
padding: new EdgeInsets.only(bottom: 20.0),
|
||||
),
|
||||
new Text(
|
||||
widget._isCorrect == true ? "Correct!" : "Wrong!",
|
||||
style: new TextStyle(color: Colors.white, fontSize: 30.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
93
lib/Screens/Sections/Puzzle/message_dialog.dart
Normal file
93
lib/Screens/Sections/Puzzle/message_dialog.dart
Normal file
@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:mymuseum_visitapp/Components/show_element_for_resource.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:mymuseum_visitapp/constants.dart';
|
||||
|
||||
void showMessage(TranslationAndResourceDTO translationAndResourceDTO, AppContext appContext, BuildContext context, Size size) {
|
||||
print("translationAndResourceDTO");
|
||||
print(translationAndResourceDTO);
|
||||
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
|
||||
showDialog(
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0))
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if(translationAndResourceDTO.resourceId != null)
|
||||
Container(
|
||||
//color: Colors.cyan,
|
||||
height: size.height *0.45,
|
||||
width: size.width *0.5,
|
||||
child: Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 30),
|
||||
//border: Border.all(width: 3, color: Colors.black)
|
||||
),
|
||||
child: showElementForResource(ResourceDTO(id: translationAndResourceDTO.resourceId, type: translationAndResourceDTO.resource!.type, url: translationAndResourceDTO.resource!.url), appContext, true, false),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
//color: Colors.green,
|
||||
height: size.height *0.3,
|
||||
width: size.width *0.5,
|
||||
child: Center(
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: HtmlWidget(
|
||||
translationAndResourceDTO.value!,
|
||||
customStylesBuilder: (element) {
|
||||
return {'text-align': 'center', 'font-family': "Roboto"};
|
||||
},
|
||||
textStyle: const TextStyle(fontSize: kDescriptionSize),
|
||||
),/*Text(
|
||||
resourceDTO.label == null ? "" : resourceDTO.label,
|
||||
style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)),*/
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
/*actions: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: Container(
|
||||
width: 175,
|
||||
height: 70,
|
||||
child: RoundedButton(
|
||||
text: "Merci",
|
||||
icon: Icons.undo,
|
||||
color: kSecondGrey,
|
||||
press: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],*/
|
||||
), context: context
|
||||
);
|
||||
}
|
||||
357
lib/Screens/Sections/Puzzle/puzzle_page.dart
Normal file
357
lib/Screens/Sections/Puzzle/puzzle_page.dart
Normal file
@ -0,0 +1,357 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:mymuseum_visitapp/Components/loading_common.dart';
|
||||
import 'package:mymuseum_visitapp/Helpers/translationHelper.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Puzzle/message_dialog.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:mymuseum_visitapp/constants.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'puzzle_piece.dart';
|
||||
|
||||
const IMAGE_PATH = 'image_path';
|
||||
|
||||
class PuzzlePage extends StatefulWidget {
|
||||
final PuzzleDTO section;
|
||||
PuzzlePage({required this.section});
|
||||
|
||||
@override
|
||||
_PuzzlePage createState() => _PuzzlePage();
|
||||
}
|
||||
|
||||
class _PuzzlePage extends State<PuzzlePage> {
|
||||
PuzzleDTO puzzleDTO = PuzzleDTO();
|
||||
|
||||
int allInPlaceCount = 0;
|
||||
bool isFinished = false;
|
||||
GlobalKey _widgetKey = GlobalKey();
|
||||
Size? realWidgetSize;
|
||||
List<Widget> pieces = [];
|
||||
|
||||
bool isSplittingImage = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
//puzzleDTO = PuzzleDTO.fromJson(jsonDecode(widget.section!.data!))!;
|
||||
puzzleDTO = widget.section;
|
||||
puzzleDTO.rows = puzzleDTO.rows ?? 3;
|
||||
puzzleDTO.cols = puzzleDTO.cols ?? 3;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
Size size = MediaQuery.of(context).size;
|
||||
final appContext = Provider.of<AppContext>(context, listen: false);
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
|
||||
print(puzzleDTO.messageDebut);
|
||||
TranslationAndResourceDTO? messageDebut = puzzleDTO.messageDebut != null && puzzleDTO.messageDebut!.isNotEmpty ? puzzleDTO.messageDebut!.where((message) => message.language!.toUpperCase() == visitAppContext.language!.toUpperCase()).firstOrNull : null;
|
||||
|
||||
//await Future.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
getRealWidgetSize();
|
||||
|
||||
if(puzzleDTO.puzzleImage != null && puzzleDTO.puzzleImage!.url != null) {
|
||||
//splitImage(Image.network(puzzleDTO.image!.resourceUrl!));
|
||||
splitImage(CachedNetworkImage(
|
||||
imageUrl: puzzleDTO.puzzleImage!.url!,
|
||||
fit: BoxFit.fill,
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
));
|
||||
} else {
|
||||
setState(() {
|
||||
isSplittingImage = false;
|
||||
});
|
||||
}
|
||||
|
||||
if(messageDebut != null) {
|
||||
showMessage(messageDebut, appContext, context, size);
|
||||
}
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> getRealWidgetSize() async {
|
||||
RenderBox renderBox = _widgetKey.currentContext?.findRenderObject() as RenderBox;
|
||||
Size size = renderBox.size;
|
||||
|
||||
setState(() {
|
||||
realWidgetSize = size;
|
||||
});
|
||||
print("Taille réelle du widget : $size");
|
||||
}
|
||||
|
||||
// we need to find out the image size, to be used in the PuzzlePiece widget
|
||||
/*Future<Size> getImageSize(CachedNetworkImage image) async {
|
||||
Completer<Size> completer = Completer<Size>();
|
||||
|
||||
/*image.image
|
||||
.resolve(const ImageConfiguration())
|
||||
.addListener(ImageStreamListener((ImageInfo info, bool _) {
|
||||
completer.complete(
|
||||
Size(info.image.width.toDouble(), info.image.height.toDouble()));
|
||||
}));*/
|
||||
|
||||
CachedNetworkImage(
|
||||
imageUrl: 'https://example.com/image.jpg',
|
||||
placeholder: (context, url) => CircularProgressIndicator(),
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
imageBuilder: (BuildContext context, ImageProvider imageProvider) {
|
||||
Completer<Size> completer = Completer<Size>();
|
||||
|
||||
imageProvider
|
||||
.resolve(const ImageConfiguration())
|
||||
.addListener(ImageStreamListener((ImageInfo info, bool _) {
|
||||
completer.complete(
|
||||
Size(info.image.width.toDouble(), info.image.height.toDouble()));
|
||||
}));
|
||||
|
||||
return CachedNetworkImage(
|
||||
imageUrl: 'https://example.com/image.jpg',
|
||||
placeholder: (context, url) => CircularProgressIndicator(),
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
imageBuilder: (context, imageProvider) {
|
||||
return Image(
|
||||
image: imageProvider,
|
||||
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child;
|
||||
} else {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded / (loadingProgress.expectedTotalBytes ?? 1)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Size imageSize = await completer.future;
|
||||
|
||||
return imageSize;
|
||||
}*/
|
||||
|
||||
// here we will split the image into small pieces
|
||||
// using the rows and columns defined above; each piece will be added to a stack
|
||||
void splitImage(CachedNetworkImage image) async {
|
||||
//Size imageSize = await getImageSize(image);
|
||||
//imageSize = realWidgetSize!;
|
||||
Size imageSize = Size(realWidgetSize!.width * 1.25, realWidgetSize!.height * 1.25);
|
||||
|
||||
for (int x = 0; x < puzzleDTO.rows!; x++) {
|
||||
for (int y = 0; y < puzzleDTO.cols!; y++) {
|
||||
setState(() {
|
||||
pieces.add(
|
||||
PuzzlePiece(
|
||||
key: GlobalKey(),
|
||||
image: image,
|
||||
imageSize: imageSize,
|
||||
row: x,
|
||||
col: y,
|
||||
maxRow: puzzleDTO.rows!,
|
||||
maxCol: puzzleDTO.cols!,
|
||||
bringToTop: bringToTop,
|
||||
sendToBack: sendToBack,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isSplittingImage = false;
|
||||
});
|
||||
}
|
||||
|
||||
// when the pan of a piece starts, we need to bring it to the front of the stack
|
||||
void bringToTop(Widget widget) {
|
||||
setState(() {
|
||||
pieces.remove(widget);
|
||||
pieces.add(widget);
|
||||
});
|
||||
}
|
||||
|
||||
// when a piece reaches its final position,
|
||||
// it will be sent to the back of the stack to not get in the way of other, still movable, pieces
|
||||
void sendToBack(Widget widget) {
|
||||
setState(() {
|
||||
allInPlaceCount++;
|
||||
isFinished = allInPlaceCount == puzzleDTO.rows! * puzzleDTO.cols!;
|
||||
pieces.remove(widget);
|
||||
pieces.insert(0, widget);
|
||||
|
||||
if(isFinished) {
|
||||
Size size = MediaQuery.of(context).size;
|
||||
final appContext = Provider.of<AppContext>(context, listen: false);
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
TranslationAndResourceDTO? messageFin = puzzleDTO.messageFin != null && puzzleDTO.messageFin!.isNotEmpty ? puzzleDTO.messageFin!.where((message) => message.language!.toUpperCase() == visitAppContext.language!.toUpperCase()).firstOrNull : null;
|
||||
|
||||
if(messageFin != null) {
|
||||
showMessage(messageFin, appContext, context, size);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
Size size = MediaQuery.of(context).size;
|
||||
var title = TranslationHelper.get(widget.section.title, appContext.getContext());
|
||||
String cleanedTitle = title.replaceAll('\n', ' ').replaceAll('<br>', ' ');
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: size.height * 0.28,
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: kMainGrey,
|
||||
spreadRadius: 0.5,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 1), // changes position of shadow
|
||||
),
|
||||
],
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.centerRight,
|
||||
end: Alignment.centerLeft,
|
||||
colors: [
|
||||
/*Color(0xFFDD79C2),
|
||||
Color(0xFFB65FBE),
|
||||
Color(0xFF9146BA),
|
||||
Color(0xFF7633B8),
|
||||
Color(0xFF6528B6),
|
||||
Color(0xFF6025B6)*/
|
||||
kMainColor0, //Color(0xFFf6b3c4)
|
||||
kMainColor1,
|
||||
kMainColor2,
|
||||
|
||||
],
|
||||
),
|
||||
image: widget.section.imageSource != null ? DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
opacity: 0.65,
|
||||
image: NetworkImage(
|
||||
widget.section.imageSource!,
|
||||
),
|
||||
): null,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: size.height * 0.11,
|
||||
width: size.width,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 22.0),
|
||||
child: SizedBox(
|
||||
width: size.width *0.7,
|
||||
child: HtmlWidget(
|
||||
cleanedTitle,
|
||||
textStyle: const TextStyle(color: Colors.white, fontFamily: 'Roboto', fontSize: 20),
|
||||
customStylesBuilder: (element)
|
||||
{
|
||||
return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"};
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 35,
|
||||
left: 10,
|
||||
child: SizedBox(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: kMainColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.arrow_back, size: 23, color: Colors.white)
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 0),
|
||||
decoration: const BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: kMainGrey,
|
||||
spreadRadius: 0.5,
|
||||
blurRadius: 2,
|
||||
offset: Offset(0, 1), // changes position of shadow
|
||||
),
|
||||
],
|
||||
color: kBackgroundColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30),
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30),
|
||||
),
|
||||
child: Center(
|
||||
//color: Colors.green,
|
||||
child: Container(
|
||||
color: Colors.green,
|
||||
child: Padding(
|
||||
key: _widgetKey,
|
||||
padding: const EdgeInsets.all(0.0),
|
||||
child: isSplittingImage ? Center(child: LoadingCommon()) :
|
||||
puzzleDTO.puzzleImage == null || puzzleDTO.puzzleImage!.url == null || realWidgetSize == null
|
||||
? Center(child: Text("Aucune image à afficher", style: TextStyle(fontSize: kNoneInfoOrIncorrect)))
|
||||
: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(0.0),
|
||||
child: Container(
|
||||
width: visitAppContext.puzzleSize != null && visitAppContext.puzzleSize!.width > 0 ? visitAppContext.puzzleSize!.width : realWidgetSize!.width * 0.8,
|
||||
height: visitAppContext.puzzleSize != null && visitAppContext.puzzleSize!.height > 0 ? visitAppContext.puzzleSize!.height +1.5 : realWidgetSize!.height * 0.85,
|
||||
child: Stack(
|
||||
children: pieces,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
287
lib/Screens/Sections/Puzzle/puzzle_piece.dart
Normal file
287
lib/Screens/Sections/Puzzle/puzzle_piece.dart
Normal file
@ -0,0 +1,287 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class PuzzlePiece extends StatefulWidget {
|
||||
final CachedNetworkImage image;
|
||||
final Size imageSize;
|
||||
final int row;
|
||||
final int col;
|
||||
final int maxRow;
|
||||
final int maxCol;
|
||||
final Function bringToTop;
|
||||
final Function sendToBack;
|
||||
|
||||
static PuzzlePiece fromMap(Map<String, dynamic> map) {
|
||||
return PuzzlePiece(
|
||||
image: map['image'],
|
||||
imageSize: map['imageSize'],
|
||||
row: map['row'],
|
||||
col: map['col'],
|
||||
maxRow: map['maxRow'],
|
||||
maxCol: map['maxCol'],
|
||||
bringToTop: map['bringToTop'],
|
||||
sendToBack: map['SendToBack'],
|
||||
);
|
||||
}
|
||||
|
||||
PuzzlePiece(
|
||||
{Key? key,
|
||||
required this.image,
|
||||
required this.imageSize,
|
||||
required this.row,
|
||||
required this.col,
|
||||
required this.maxRow,
|
||||
required this.maxCol,
|
||||
required this.bringToTop,
|
||||
required this.sendToBack})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_PuzzlePieceState createState() => _PuzzlePieceState();
|
||||
}
|
||||
|
||||
class _PuzzlePieceState extends State<PuzzlePiece> {
|
||||
// the piece initial top offset
|
||||
double? top;
|
||||
// the piece initial left offset
|
||||
double? left;
|
||||
// can we move the piece ?
|
||||
bool isMovable = true;
|
||||
|
||||
GlobalKey _widgetPieceKey = GlobalKey();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
|
||||
RenderBox renderBox = _widgetPieceKey.currentContext?.findRenderObject() as RenderBox;
|
||||
Size size = renderBox.size;
|
||||
|
||||
final appContext = Provider.of<AppContext>(context, listen: false);
|
||||
VisitAppContext visitAppContext = appContext.getContext();
|
||||
visitAppContext.puzzleSize = size; // do it another way
|
||||
appContext.setContext(visitAppContext);
|
||||
|
||||
if(widget.row == 0 && widget.col == 0) {
|
||||
widget.sendToBack(widget);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
var isPortrait = MediaQuery.of(context).orientation == Orientation.portrait;
|
||||
|
||||
var imageHeight = isPortrait ? widget.imageSize.width/1.55 : widget.imageSize.width;
|
||||
var imageWidth = isPortrait ? widget.imageSize.width/1.55 : widget.imageSize.height;
|
||||
|
||||
final pieceWidth = imageWidth / widget.maxCol;
|
||||
final pieceHeight = imageHeight / widget.maxRow;
|
||||
|
||||
if (top == null) {
|
||||
top = Random().nextInt((imageHeight - pieceHeight).ceil()).toDouble();
|
||||
var test = top!;
|
||||
test -= widget.row * pieceHeight;
|
||||
top = test /7; // TODO change ?
|
||||
}
|
||||
|
||||
if (left == null) {
|
||||
left = Random().nextInt((imageWidth - pieceWidth).ceil()).toDouble();
|
||||
var test = left!;
|
||||
test -= widget.col * pieceWidth;
|
||||
left = test /7; // TODO change ?
|
||||
}
|
||||
|
||||
if(widget.row == 0 && widget.col == 0) {
|
||||
top = 0;
|
||||
left = 0;
|
||||
isMovable = false;
|
||||
}
|
||||
|
||||
return Positioned(
|
||||
top: top,
|
||||
left: left,
|
||||
width: imageWidth,
|
||||
child: Container(
|
||||
key: _widgetPieceKey,
|
||||
decoration: widget.col == 0 && widget.row == 0 ? BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.black,
|
||||
width: 0.5,
|
||||
),
|
||||
) : null,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (isMovable) {
|
||||
widget.bringToTop(widget);
|
||||
}
|
||||
},
|
||||
onPanStart: (_) {
|
||||
if (isMovable) {
|
||||
widget.bringToTop(widget);
|
||||
}
|
||||
},
|
||||
onPanUpdate: (dragUpdateDetails) {
|
||||
if (isMovable) {
|
||||
setState(() {
|
||||
var testTop = top!;
|
||||
var testLeft = left!;
|
||||
testTop = top!;
|
||||
testLeft = left!;
|
||||
testTop += dragUpdateDetails.delta.dy;
|
||||
testLeft += dragUpdateDetails.delta.dx;
|
||||
top = testTop;
|
||||
left = testLeft;
|
||||
|
||||
if (-10 < top! && top! < 10 && -10 < left! && left! < 10) {
|
||||
top = 0;
|
||||
left = 0;
|
||||
isMovable = false;
|
||||
widget.sendToBack(widget);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
child: ClipPath(
|
||||
child: CustomPaint(
|
||||
foregroundPainter: PuzzlePiecePainter(
|
||||
widget.row, widget.col, widget.maxRow, widget.maxCol),
|
||||
child: widget.image),
|
||||
clipper: PuzzlePieceClipper(
|
||||
widget.row, widget.col, widget.maxRow, widget.maxCol),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// this class is used to clip the image to the puzzle piece path
|
||||
class PuzzlePieceClipper extends CustomClipper<Path> {
|
||||
final int row;
|
||||
final int col;
|
||||
final int maxRow;
|
||||
final int maxCol;
|
||||
|
||||
PuzzlePieceClipper(this.row, this.col, this.maxRow, this.maxCol);
|
||||
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
return getPiecePath(size, row, col, maxRow, maxCol);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
|
||||
}
|
||||
|
||||
// this class is used to draw a border around the clipped image
|
||||
class PuzzlePiecePainter extends CustomPainter {
|
||||
final int row;
|
||||
final int col;
|
||||
final int maxRow;
|
||||
final int maxCol;
|
||||
|
||||
PuzzlePiecePainter(this.row, this.col, this.maxRow, this.maxCol);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint paint = Paint()
|
||||
..color = Colors.black//Color(0x80FFFFFF)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.5;
|
||||
|
||||
canvas.drawPath(getPiecePath(size, row, col, maxRow, maxCol), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// this is the path used to clip the image and, then, to draw a border around it; here we actually draw the puzzle piece
|
||||
Path getPiecePath(Size size, int row, int col, int maxRow, int maxCol) {
|
||||
final width = size.width / maxCol;
|
||||
final height = size.height / maxRow;
|
||||
final offsetX = col * width;
|
||||
final offsetY = row * height;
|
||||
final bumpSize = height / 4;
|
||||
|
||||
var path = Path();
|
||||
path.moveTo(offsetX, offsetY);
|
||||
|
||||
if (row == 0) {
|
||||
// top side piece
|
||||
path.lineTo(offsetX + width, offsetY);
|
||||
} else {
|
||||
// top bump
|
||||
path.lineTo(offsetX + width / 3, offsetY);
|
||||
path.cubicTo(
|
||||
offsetX + width / 6,
|
||||
offsetY - bumpSize,
|
||||
offsetX + width / 6 * 5,
|
||||
offsetY - bumpSize,
|
||||
offsetX + width / 3 * 2,
|
||||
offsetY);
|
||||
path.lineTo(offsetX + width, offsetY);
|
||||
}
|
||||
|
||||
if (col == maxCol - 1) {
|
||||
// right side piece
|
||||
path.lineTo(offsetX + width, offsetY + height);
|
||||
} else {
|
||||
// right bump
|
||||
path.lineTo(offsetX + width, offsetY + height / 3);
|
||||
path.cubicTo(
|
||||
offsetX + width - bumpSize,
|
||||
offsetY + height / 6,
|
||||
offsetX + width - bumpSize,
|
||||
offsetY + height / 6 * 5,
|
||||
offsetX + width,
|
||||
offsetY + height / 3 * 2);
|
||||
path.lineTo(offsetX + width, offsetY + height);
|
||||
}
|
||||
|
||||
if (row == maxRow - 1) {
|
||||
// bottom side piece
|
||||
path.lineTo(offsetX, offsetY + height);
|
||||
} else {
|
||||
// bottom bump
|
||||
path.lineTo(offsetX + width / 3 * 2, offsetY + height);
|
||||
path.cubicTo(
|
||||
offsetX + width / 6 * 5,
|
||||
offsetY + height - bumpSize,
|
||||
offsetX + width / 6,
|
||||
offsetY + height - bumpSize,
|
||||
offsetX + width / 3,
|
||||
offsetY + height);
|
||||
path.lineTo(offsetX, offsetY + height);
|
||||
}
|
||||
|
||||
if (col == 0) {
|
||||
// left side piece
|
||||
path.close();
|
||||
} else {
|
||||
// left bump
|
||||
path.lineTo(offsetX, offsetY + height / 3 * 2);
|
||||
path.cubicTo(
|
||||
offsetX - bumpSize,
|
||||
offsetY + height / 6 * 5,
|
||||
offsetX - bumpSize,
|
||||
offsetY + height / 6,
|
||||
offsetX,
|
||||
offsetY + height / 3);
|
||||
path.close();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
14
lib/Screens/Sections/Puzzle/score_widget.dart
Normal file
14
lib/Screens/Sections/Puzzle/score_widget.dart
Normal file
@ -0,0 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScoreWidget extends InheritedWidget {
|
||||
ScoreWidget({Key? key, required Widget child}) : super(key: key, child: child);
|
||||
|
||||
int allInPlaceCount = 0;
|
||||
|
||||
static ScoreWidget of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ScoreWidget>() as ScoreWidget;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ScoreWidget oldWidget) => false;
|
||||
}
|
||||
336
lib/Screens/Sections/Slider/slider_view.dart
Normal file
336
lib/Screens/Sections/Slider/slider_view.dart
Normal file
@ -0,0 +1,336 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:manager_api_new/api.dart';
|
||||
import 'package:mymuseum_visitapp/Components/show_element_for_resource.dart';
|
||||
import 'package:mymuseum_visitapp/Helpers/ImageCustomProvider.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:mymuseum_visitapp/constants.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||
|
||||
class SliderPage extends StatefulWidget {
|
||||
final SliderDTO section;
|
||||
SliderPage({required this.section});
|
||||
|
||||
@override
|
||||
_SliderPage createState() => _SliderPage();
|
||||
}
|
||||
|
||||
class _SliderPage extends State<SliderPage> {
|
||||
SliderDTO sliderDTO = SliderDTO();
|
||||
CarouselSliderController? sliderController;
|
||||
ValueNotifier<int> currentIndex = ValueNotifier<int>(1);
|
||||
|
||||
late ConfigurationDTO configurationDTO;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
sliderController = CarouselSliderController();
|
||||
sliderDTO = widget.section;
|
||||
sliderDTO.contents!.sort((a, b) => a.order!.compareTo(b.order!));
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
sliderController = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Size size = MediaQuery.of(context).size;
|
||||
final appContext = Provider.of<AppContext>(context);
|
||||
VisitAppContext visitAppContex = appContext.getContext() as VisitAppContext;
|
||||
Color? primaryColor = visitAppContex.configuration!.primaryColor != null ? Color(int.parse(visitAppContex.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : null;
|
||||
|
||||
configurationDTO = appContext.getContext().configuration;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
if(sliderDTO.contents != null && sliderDTO.contents!.isNotEmpty)
|
||||
CarouselSlider(
|
||||
carouselController: sliderController,
|
||||
options: CarouselOptions(
|
||||
onPageChanged: (int index, CarouselPageChangedReason reason) {
|
||||
currentIndex.value = index + 1;
|
||||
},
|
||||
height: MediaQuery.of(context).size.height * 1.0,
|
||||
enlargeCenterPage: false,
|
||||
reverse: false,
|
||||
),
|
||||
items: sliderDTO.contents!.map<Widget>((i) {
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green,
|
||||
//color: configurationDTO.imageId == null ? configurationDTO.secondaryColor != null ? new Color(int.parse(configurationDTO.secondaryColor!.split('(0x')[1].split(')')[0], radix: 16)): kBackgroundGrey : null,
|
||||
borderRadius: BorderRadius.circular(visitAppContex.configuration!.roundedValue?.toDouble() ?? 10.0),
|
||||
//border: Border.all(width: 0.3, color: kSecondGrey),
|
||||
),
|
||||
child: Column(
|
||||
//crossAxisAlignment: CrossAxisAlignment.center,
|
||||
//mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Container(
|
||||
color: Colors.orange,
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
width: MediaQuery.of(context).size.width * 0.72,
|
||||
/*decoration: BoxDecoration(
|
||||
color: kBackgroundLight,
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
/*image: i.source_ != null ? new DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: new NetworkImage(
|
||||
i.source_,
|
||||
),
|
||||
): null,*/
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: kBackgroundSecondGrey,
|
||||
spreadRadius: 0.5,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 1.5), // changes position of shadow
|
||||
),
|
||||
],
|
||||
),*/
|
||||
child: Stack(
|
||||
children: [
|
||||
getElementForResource(appContext, i),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: HtmlWidget(
|
||||
i.title!.where((translation) => translation.language == appContext.getContext().language).firstOrNull?.value != null ? i.title!.firstWhere((translation) => translation.language == appContext.getContext().language).value! : "",
|
||||
textStyle: const TextStyle(fontSize: kTitleSize, color: kBackgroundLight),
|
||||
),
|
||||
)
|
||||
)
|
||||
]
|
||||
),/**/
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height *0.25,
|
||||
width: MediaQuery.of(context).size.width *0.7,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,// kBackgroundLight,
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(visitAppContex.configuration!.roundedValue?.toDouble() ?? 10.0),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: kBackgroundSecondGrey,
|
||||
spreadRadius: 0.3,
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2), // changes position of shadow
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: HtmlWidget(
|
||||
i.description!.where((translation) => translation.language == appContext.getContext().language).firstOrNull?.value != null ? i.description!.firstWhere((translation) => translation.language == appContext.getContext().language).value! : "",
|
||||
textStyle: const TextStyle(fontSize: kDescriptionSize),
|
||||
customStylesBuilder: (element) {
|
||||
return {'text-align': 'center', 'font-family': "Roboto"};
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
/*if(sliderDTO.contents != null && sliderDTO.contents!.length > 1)
|
||||
Positioned(
|
||||
top: MediaQuery.of(context).size.height * 0.35,
|
||||
right: 60,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (sliderDTO.contents!.length > 0)
|
||||
sliderController!.nextPage(duration: new Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.chevron_right,
|
||||
size: 90,
|
||||
color: primaryColor ?? kMainColor,
|
||||
),
|
||||
)
|
||||
),*/
|
||||
/*if(sliderDTO.contents != null && sliderDTO.contents!.length > 1)
|
||||
Positioned(
|
||||
top: MediaQuery.of(context).size.height * 0.35,
|
||||
left: 60,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (sliderDTO.contents!.length > 0)
|
||||
sliderController!.previousPage(duration: new Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.chevron_left,
|
||||
size: 90,
|
||||
color: primaryColor ?? kMainColor,
|
||||
),
|
||||
)
|
||||
),*/
|
||||
if(sliderDTO.contents != null && sliderDTO.contents!.isNotEmpty) // Todo replace by dot ?
|
||||
Padding(
|
||||
padding: widget.section.parentId == null ? const EdgeInsets.only(bottom: 20) : const EdgeInsets.only(left: 15, bottom: 20),
|
||||
child: Align(
|
||||
alignment: widget.section.parentId == null ? Alignment.bottomCenter : Alignment.bottomLeft,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
sliderController!.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
|
||||
},
|
||||
child: ValueListenableBuilder<int>(
|
||||
valueListenable: currentIndex,
|
||||
builder: (context, value, _) {
|
||||
return AnimatedSmoothIndicator(
|
||||
activeIndex: value -1,
|
||||
count: sliderDTO.contents!.length,
|
||||
effect: const ExpandingDotsEffect(activeDotColor: kMainColor),
|
||||
);
|
||||
|
||||
/*Text(
|
||||
value.toString()+'/'+sliderDTO.contents!.length.toString(),
|
||||
style: const TextStyle(fontSize: 25, fontWeight: FontWeight.w500),
|
||||
);*/
|
||||
}
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
if(sliderDTO.contents == null || sliderDTO.contents!.isEmpty)
|
||||
const Center(child: Text("Aucun contenu à afficher", style: TextStyle(fontSize: kNoneInfoOrIncorrect))),
|
||||
Positioned(
|
||||
top: 35,
|
||||
left: 10,
|
||||
child: SizedBox(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: kMainColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.arrow_back, size: 23, color: Colors.white)
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
// Description
|
||||
/*Container(
|
||||
height: sliderDTO.images != null && sliderDTO.images.length > 0 ? size.height *0.3 : size.height *0.6,
|
||||
width: MediaQuery.of(context).size.width *0.35,
|
||||
decoration: BoxDecoration(
|
||||
color: kBackgroundLight,
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: kBackgroundSecondGrey,
|
||||
spreadRadius: 0.5,
|
||||
blurRadius: 1.1,
|
||||
offset: Offset(0, 1.1), // changes position of shadow
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Text(sliderDTO., textAlign: TextAlign.center, style: TextStyle(fontSize: 15)),
|
||||
),
|
||||
),
|
||||
),*/
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
getElementForResource(AppContext appContext, ContentDTO i) {
|
||||
var widgetToInclude;
|
||||
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
|
||||
|
||||
switch(i.resource!.type) {
|
||||
case ResourceType.Image:
|
||||
widgetToInclude = PhotoView(
|
||||
imageProvider: ImageCustomProvider.getImageProvider(appContext, i.resourceId!, i.resource!.url!),
|
||||
minScale: PhotoViewComputedScale.contained * 0.8,
|
||||
maxScale: PhotoViewComputedScale.contained * 3.0,
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: kBackgroundSecondGrey,
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case ResourceType.ImageUrl:
|
||||
widgetToInclude = PhotoView(
|
||||
imageProvider: CachedNetworkImageProvider(i.resource!.url!),
|
||||
minScale: PhotoViewComputedScale.contained * 0.8,
|
||||
maxScale: PhotoViewComputedScale.contained * 3.0,
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: kBackgroundSecondGrey,
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case ResourceType.Video:
|
||||
case ResourceType.VideoUrl:
|
||||
case ResourceType.Audio:
|
||||
widgetToInclude = Container(
|
||||
decoration: BoxDecoration(
|
||||
//color: kBackgroundSecondGrey,
|
||||
//shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
|
||||
),
|
||||
child: showElementForResource(ResourceDTO(id: i.resourceId, url: i.resource!.url, type: i.resource!.type), appContext, false, true),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
width: MediaQuery.of(context).size.width * 0.72,
|
||||
color: Colors.yellow,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: ClipRect(
|
||||
child: widgetToInclude,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -15,8 +15,10 @@ import 'package:mymuseum_visitapp/Models/resourceModel.dart';
|
||||
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Article/article_page.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/PDF/pdf_view.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Puzzle/puzzle_page.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Quiz/quizz_page.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Video/video_view.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Slider/slider_view.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Video/video_page.dart';
|
||||
import 'package:mymuseum_visitapp/Screens/Sections/Web/web_page.dart';
|
||||
import 'package:mymuseum_visitapp/app_context.dart';
|
||||
import 'package:mymuseum_visitapp/client.dart';
|
||||
@ -64,7 +66,7 @@ class _SectionPageState extends State<SectionPage> {
|
||||
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: test!.type != SectionType.Quiz && test.type != SectionType.Article && test.type != SectionType.Web && test.type != SectionType.Pdf && test.type != SectionType.Video ? CustomAppBar(
|
||||
appBar: test!.type != SectionType.Quiz && test.type != SectionType.Article && test.type != SectionType.Web && test.type != SectionType.Pdf && test.type != SectionType.Video && test.type != SectionType.Puzzle && test.type != SectionType.Slider ? CustomAppBar(
|
||||
title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "",
|
||||
isHomeButton: false,
|
||||
) : null,
|
||||
@ -91,6 +93,12 @@ class _SectionPageState extends State<SectionPage> {
|
||||
case SectionType.Video:
|
||||
VideoDTO videoDTO = VideoDTO.fromJson(sectionResult)!;
|
||||
return VideoPage(section: videoDTO);
|
||||
case SectionType.Puzzle:
|
||||
PuzzleDTO puzzleDTO = PuzzleDTO.fromJson(sectionResult)!;
|
||||
return PuzzlePage(section: puzzleDTO);
|
||||
case SectionType.Slider:
|
||||
SliderDTO sliderDTO = SliderDTO.fromJson(sectionResult)!;
|
||||
return SliderPage(section: sliderDTO);
|
||||
default:
|
||||
return const Center(child: Text("Unsupported type"));
|
||||
}
|
||||
|
||||
@ -1081,6 +1081,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
smooth_page_indicator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: smooth_page_indicator
|
||||
sha256: b21ebb8bc39cf72d11c7cfd809162a48c3800668ced1c9da3aade13a32cf6c1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -61,6 +61,8 @@ dependencies:
|
||||
flutter_beacon: ^0.5.1 #not in web
|
||||
flutter_staggered_grid_view: ^0.7.0
|
||||
|
||||
smooth_page_indicator: ^1.2.1
|
||||
|
||||
manager_api_new:
|
||||
path: manager_api_new
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user