Handle resource viewer + add rows and cols to puzzle

This commit is contained in:
Thomas Fransolet 2023-12-29 14:59:21 +01:00
parent ce815f9950
commit 1d81997a99
20 changed files with 380 additions and 74 deletions

View File

@ -30,7 +30,7 @@ class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
style: TextStyle(fontSize: 20, color: kBlack), style: TextStyle(fontSize: 20, color: kBlack),
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Mot de passe", hintText: "Mot de passe",
hintStyle: TextStyle(fontSize: 20.0, color: kBlack), hintStyle: TextStyle(fontSize: 20.0, color: kBlack, fontWeight: FontWeight.normal),
icon: Icon( icon: Icon(
Icons.lock, Icons.lock,
color: kPrimaryColor, color: kPrimaryColor,

View File

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:manager_app/Components/loading_common.dart';
import 'package:video_player/video_player.dart';
import '../../constants.dart';
class VideoViewer extends StatefulWidget {
final String videoUrl;
VideoViewer({required this.videoUrl});
@override
_VideoViewer createState() => _VideoViewer();
}
class _VideoViewer extends State<VideoViewer> {
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.networkUrl(Uri.parse(
widget.videoUrl))
..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: kPrimaryColor.withOpacity(0.8),
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white),
),
)
],
);
}
}

View File

@ -0,0 +1,91 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:youtube_player_iframe/youtube_player_iframe.dart' as iframe;
import 'package:youtube_player_iframe/youtube_player_iframe.dart';
class VideoViewerYoutube extends StatefulWidget {
final String videoUrl;
VideoViewerYoutube({required this.videoUrl});
@override
_VideoViewerYoutube createState() => _VideoViewerYoutube();
}
class _VideoViewerYoutube extends State<VideoViewerYoutube> {
iframe.YoutubePlayer? _videoViewWeb;
YoutubePlayer? _videoView;
@override
void initState() {
//String? videoId;
if (widget.videoUrl.length > 0 ) {
//videoId = YoutubePlayer.convertUrlToId(widget.videoUrl);
//if (kIsWeb) {
final _controllerWeb = iframe.YoutubePlayerController(
params: iframe.YoutubePlayerParams(
mute: false,
showControls: true,
showFullscreenButton: false,
loop: true,
showVideoAnnotations: false,
strictRelatedVideos: false,
enableKeyboard: false,
enableCaption: false,
pointerEvents: iframe.PointerEvents.auto
),
);
_controllerWeb.loadVideo(widget.videoUrl);
_videoViewWeb = iframe.YoutubePlayer(
controller: _controllerWeb,
//showVideoProgressIndicator: false,
/*progressIndicatorColor: Colors.amber,
progressColors: ProgressBarColors(
playedColor: Colors.amber,
handleColor: Colors.amberAccent,
),*/
);
//} else {
/*videoId = YoutubePlayer.convertUrlToId(widget.videoUrl);
YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: videoId!,
flags: YoutubePlayerFlags(
autoPlay: true,
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.length > 0 ?
(kIsWeb ? _videoViewWeb! : _videoView!):
Center(child: Text("La vidéo ne peut pas être affichée, l'url est incorrecte", style: new TextStyle(fontSize: 12)));
}

View File

@ -1,7 +1,9 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Components/multi_string_input_and_resource_container.dart'; import 'package:manager_app/Components/multi_string_input_and_resource_container.dart';
import 'package:manager_app/Components/multi_string_input_container.dart'; import 'package:manager_app/Components/multi_string_input_container.dart';
import 'package:manager_app/Components/number_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
import 'dart:convert'; import 'dart:convert';
@ -35,70 +37,125 @@ class _PuzzleConfigState extends State<PuzzleConfig> {
test.image = PuzzleDTOImage(); test.image = PuzzleDTOImage();
} }
puzzleDTO = test; puzzleDTO = test;
puzzleDTO.rows = puzzleDTO.rows == null ? 3 : puzzleDTO.rows;
puzzleDTO.cols = puzzleDTO.cols == null ? 3 : puzzleDTO.cols;
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Center(
mainAxisAlignment: MainAxisAlignment.spaceAround, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
ResourceInputContainer( children: [
label: "Image du puzzle :", Row(
initialValue: puzzleDTO.image!.resourceId == null ? '': puzzleDTO.image!.resourceId, mainAxisAlignment: MainAxisAlignment.spaceAround,
onChanged: (ResourceDTO resourceDTO) { children: [
setState(() { ResourceInputContainer(
puzzleDTO.image!.resourceId = resourceDTO.id; label: "Image du puzzle :",
puzzleDTO.image!.resourceType = resourceDTO.type; initialValue: puzzleDTO.image!.resourceId == null ? '': puzzleDTO.image!.resourceId,
puzzleDTO.image!.resourceUrl = resourceDTO.url; onChanged: (ResourceDTO resourceDTO) {
print(puzzleDTO.image);
widget.onChanged(jsonEncode(puzzleDTO).toString());
});
}
),
Container(
height: 100,
child: MultiStringInputAndResourceContainer(
label: "Message départ :",
modalLabel: "Message départ",
fontSize: 20,
color: kPrimaryColor,
initialValue: puzzleDTO.messageDebut != null ? puzzleDTO.messageDebut! : [],
onGetResult: (value) {
print("Mess depart test");
print(value);
if (puzzleDTO.messageDebut != value) {
setState(() { setState(() {
puzzleDTO.messageDebut = value; puzzleDTO.image!.resourceId = resourceDTO.id;
puzzleDTO.image!.resourceType = resourceDTO.type;
puzzleDTO.image!.resourceUrl = resourceDTO.url;
print(puzzleDTO.image);
widget.onChanged(jsonEncode(puzzleDTO).toString()); widget.onChanged(jsonEncode(puzzleDTO).toString());
}); });
} }
}, ),
maxLines: 1, Container(
isTitle: false height: 100,
) child: MultiStringInputAndResourceContainer(
), label: "Message départ :",
Container( modalLabel: "Message départ",
height: 100, fontSize: 20,
child: MultiStringInputAndResourceContainer( color: kPrimaryColor,
label: "Message fin :", initialValue: puzzleDTO.messageDebut != null ? puzzleDTO.messageDebut! : [],
modalLabel: "Message fin", onGetResult: (value) {
fontSize: 20, print("Mess depart test");
color: kPrimaryColor, print(value);
initialValue: puzzleDTO.messageFin != null ? puzzleDTO.messageFin! : [], if (puzzleDTO.messageDebut != value) {
onGetResult: (value) { setState(() {
if (puzzleDTO.messageFin != value) { print(puzzleDTO.messageDebut);
setState(() { puzzleDTO.messageDebut = value;
puzzleDTO.messageFin = value; widget.onChanged(jsonEncode(puzzleDTO).toString());
widget.onChanged(jsonEncode(puzzleDTO).toString()); });
}); }
} },
}, maxLines: 1,
maxLines: 1, isTitle: false
isTitle: false )
) ),
), Container(
], height: 100,
child: MultiStringInputAndResourceContainer(
label: "Message fin :",
modalLabel: "Message fin",
fontSize: 20,
color: kPrimaryColor,
initialValue: puzzleDTO.messageFin != null ? puzzleDTO.messageFin! : [],
onGetResult: (value) {
if (puzzleDTO.messageFin != value) {
setState(() {
puzzleDTO.messageFin = value;
widget.onChanged(jsonEncode(puzzleDTO).toString());
});
}
},
maxLines: 1,
isTitle: false
)
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
height: 100,
child: NumberInputContainer(
label: "Nombre de lignes :",
initialValue: puzzleDTO.rows!,
isSmall: true,
maxLength: 2,
onChanged: (value) {
try {
puzzleDTO.rows = int.parse(value);
setState(() {
widget.onChanged(jsonEncode(puzzleDTO).toString());
});
} catch (e) {
print('Puzzle rows not a number');
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null);
}
},
),
),
Container(
height: 100,
child: NumberInputContainer(
label: "Nombre de colonnes :",
initialValue: puzzleDTO.cols!,
isSmall: true,
maxLength: 2,
onChanged: (value) {
try {
puzzleDTO.cols = int.parse(value);
setState(() {
widget.onChanged(jsonEncode(puzzleDTO).toString());
});
} catch (e) {
print('Puzzle rows not a number');
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null);
}
},
),
),
],)
],
),
); );
} }
} }

View File

@ -206,14 +206,14 @@ class _SectionDetailScreenState extends State<SectionDetailScreen> {
}); });
}, },
), ),
if(sectionDTO!.isBeacon!) if(sectionDTO.isBeacon!)
NumberInputContainer( NumberInputContainer(
label: "Identifiant Beacon :", label: "Identifiant Beacon :",
initialValue: sectionDTO!.beaconId != null ? sectionDTO.beaconId! : 0, initialValue: sectionDTO.beaconId != null ? sectionDTO.beaconId! : 0,
isSmall: true, isSmall: true,
onChanged: (value) { onChanged: (value) {
try { try {
sectionDTO!.beaconId = int.parse(value); sectionDTO.beaconId = int.parse(value);
} catch (e) { } catch (e) {
print('BeaconId not a number'); print('BeaconId not a number');
showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null); showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null);

View File

@ -3,6 +3,8 @@ import 'dart:typed_data';
import 'package:manager_app/Components/audio_player.dart'; import 'package:manager_app/Components/audio_player.dart';
import 'package:manager_app/Components/loading_common.dart'; import 'package:manager_app/Components/loading_common.dart';
import 'package:manager_app/Components/video_viewer.dart';
import 'package:manager_app/Components/video_viewer_youtube.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/app_context.dart'; import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
@ -84,11 +86,25 @@ getElementForResource(dynamic resourceDTO, AppContext appContext) {
);*/ );*/
//return Text("Fichier audio - aucune visualisation possible"); //return Text("Fichier audio - aucune visualisation possible");
case ResourceType.Video: case ResourceType.Video:
return Text("Vidéo locale - aucune visualisation possible"); if(resourceDTO.url == null) {
return Center(child: Text("Error loading video"));
} else {
return VideoViewer(videoUrl: resourceDTO.url!);
}
case ResourceType.VideoUrl: case ResourceType.VideoUrl:
return Text(resourceDTO.url); return SelectableText(resourceDTO.url!);
/*case ResourceType.VideoUrl:
if(resourceDTO.url == null) {
return Center(child: Text("Error loading video"));
} else {
return VideoViewerYoutube(videoUrl: resourceDTO.url!);
}*/ // TODO
case ResourceType.Pdf: case ResourceType.Pdf:
return Text("Fichier pdf - aucune visualisation possible"); return Text("Fichier pdf - aucune visualisation possible");
case ResourceType.Json: case ResourceType.Json:
return Text("Fichier json - aucune visualisation possible"); return Text("Fichier json - aucune visualisation possible");
} }

View File

@ -5,7 +5,7 @@ info:
description: API Manager Service description: API Manager Service
version: Version Alpha version: Version Alpha
servers: servers:
- url: http://localhost:5000 - url: https://api.myinfomate.be
paths: paths:
/api/Configuration: /api/Configuration:
get: get:
@ -2400,6 +2400,12 @@ components:
nullable: true nullable: true
oneOf: oneOf:
- $ref: '#/components/schemas/ContentDTO' - $ref: '#/components/schemas/ContentDTO'
rows:
type: integer
format: int32
cols:
type: integer
format: int32
AgendaDTO: AgendaDTO:
type: object type: object
additionalProperties: false additionalProperties: false

View File

@ -60,7 +60,7 @@ try {
## Documentation for API Endpoints ## Documentation for API Endpoints
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Class | Method | HTTP request | Description Class | Method | HTTP request | Description
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -11,6 +11,8 @@ Name | Type | Description | Notes
**messageDebut** | [**List<TranslationAndResourceDTO>**](TranslationAndResourceDTO.md) | | [optional] [default to const []] **messageDebut** | [**List<TranslationAndResourceDTO>**](TranslationAndResourceDTO.md) | | [optional] [default to const []]
**messageFin** | [**List<TranslationAndResourceDTO>**](TranslationAndResourceDTO.md) | | [optional] [default to const []] **messageFin** | [**List<TranslationAndResourceDTO>**](TranslationAndResourceDTO.md) | | [optional] [default to const []]
**image** | [**PuzzleDTOImage**](PuzzleDTOImage.md) | | [optional] **image** | [**PuzzleDTOImage**](PuzzleDTOImage.md) | | [optional]
**rows** | **int** | | [optional]
**cols** | **int** | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -5,7 +5,7 @@
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
``` ```
All URIs are relative to *http://localhost:5000* All URIs are relative to *https://api.myinfomate.be*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------

View File

@ -11,7 +11,7 @@
part of openapi.api; part of openapi.api;
class ApiClient { class ApiClient {
ApiClient({this.basePath = 'http://localhost:5000', this.authentication,}); ApiClient({this.basePath = 'https://api.myinfomate.be', this.authentication,});
final String basePath; final String basePath;
final Authentication? authentication; final Authentication? authentication;

View File

@ -16,6 +16,8 @@ class PuzzleDTO {
this.messageDebut = const [], this.messageDebut = const [],
this.messageFin = const [], this.messageFin = const [],
this.image, this.image,
this.rows,
this.cols,
}); });
List<TranslationAndResourceDTO>? messageDebut; List<TranslationAndResourceDTO>? messageDebut;
@ -24,21 +26,41 @@ class PuzzleDTO {
PuzzleDTOImage? image; PuzzleDTOImage? image;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
int? rows;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
int? cols;
@override @override
bool operator ==(Object other) => identical(this, other) || other is PuzzleDTO && bool operator ==(Object other) => identical(this, other) || other is PuzzleDTO &&
other.messageDebut == messageDebut && other.messageDebut == messageDebut &&
other.messageFin == messageFin && other.messageFin == messageFin &&
other.image == image; other.image == image &&
other.rows == rows &&
other.cols == cols;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(messageDebut == null ? 0 : messageDebut!.hashCode) + (messageDebut == null ? 0 : messageDebut!.hashCode) +
(messageFin == null ? 0 : messageFin!.hashCode) + (messageFin == null ? 0 : messageFin!.hashCode) +
(image == null ? 0 : image!.hashCode); (image == null ? 0 : image!.hashCode) +
(rows == null ? 0 : rows!.hashCode) +
(cols == null ? 0 : cols!.hashCode);
@override @override
String toString() => 'PuzzleDTO[messageDebut=$messageDebut, messageFin=$messageFin, image=$image]'; String toString() => 'PuzzleDTO[messageDebut=$messageDebut, messageFin=$messageFin, image=$image, rows=$rows, cols=$cols]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -57,6 +79,16 @@ class PuzzleDTO {
} else { } else {
json[r'image'] = null; json[r'image'] = null;
} }
if (this.rows != null) {
json[r'rows'] = this.rows;
} else {
json[r'rows'] = null;
}
if (this.cols != null) {
json[r'cols'] = this.cols;
} else {
json[r'cols'] = null;
}
return json; return json;
} }
@ -82,6 +114,8 @@ class PuzzleDTO {
messageDebut: TranslationAndResourceDTO.listFromJson(json[r'messageDebut']), messageDebut: TranslationAndResourceDTO.listFromJson(json[r'messageDebut']),
messageFin: TranslationAndResourceDTO.listFromJson(json[r'messageFin']), messageFin: TranslationAndResourceDTO.listFromJson(json[r'messageFin']),
image: PuzzleDTOImage.fromJson(json[r'image']), image: PuzzleDTOImage.fromJson(json[r'image']),
rows: mapValueOfType<int>(json, r'rows'),
cols: mapValueOfType<int>(json, r'cols'),
); );
} }
return null; return null;

View File

@ -1388,6 +1388,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
youtube_player_iframe:
dependency: "direct main"
description:
name: youtube_player_iframe
sha256: d7aec9083430db4e5da83a3b5d7b7fcbb93cfa027d9f680ce3c7e7cd20724305
url: "https://pub.dev"
source: hosted
version: "4.0.4"
youtube_player_iframe_web:
dependency: transitive
description:
name: youtube_player_iframe_web
sha256: c7020816031600349b56d2729d4e8be011fcb723ff7dc2dd0cdf72096a0e5ff4
url: "https://pub.dev"
source: hosted
version: "2.0.2"
sdks: sdks:
dart: ">=3.2.0-194.0.dev <4.0.0" dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.13.0" flutter: ">=3.13.0"

View File

@ -44,6 +44,7 @@ dependencies:
just_audio: ^0.9.35 just_audio: ^0.9.35
pdf: ^3.10.4 pdf: ^3.10.4
multi_select_flutter: ^4.1.3 multi_select_flutter: ^4.1.3
youtube_player_iframe: ^4.0.4
#msix: ^2.1.3 #msix: ^2.1.3
#window_size: #window_size:
# git: # git: