From 1d81997a990689c73f6ff2218bd7b449db696a5d Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Fri, 29 Dec 2023 14:59:21 +0100 Subject: [PATCH] Handle resource viewer + add rows and cols to puzzle --- lib/Components/rounded_password_field.dart | 2 +- lib/Components/video_viewer.dart | 83 +++++++++ lib/Components/video_viewer_youtube.dart | 91 ++++++++++ .../SubSection/Puzzle/puzzle_config.dart | 167 ++++++++++++------ .../Section/section_detail_screen.dart | 6 +- .../Resources/get_element_for_resource.dart | 20 ++- lib/api/swagger.yaml | 8 +- manager_api_new/README.md | 2 +- manager_api_new/doc/AuthenticationApi.md | 2 +- manager_api_new/doc/ConfigurationApi.md | 2 +- manager_api_new/doc/DeviceApi.md | 2 +- manager_api_new/doc/InstanceApi.md | 2 +- manager_api_new/doc/PuzzleDTO.md | 2 + manager_api_new/doc/ResourceApi.md | 2 +- manager_api_new/doc/SectionApi.md | 2 +- manager_api_new/doc/UserApi.md | 2 +- manager_api_new/lib/api_client.dart | 2 +- manager_api_new/lib/model/puzzle_dto.dart | 40 ++++- pubspec.lock | 16 ++ pubspec.yaml | 1 + 20 files changed, 380 insertions(+), 74 deletions(-) create mode 100644 lib/Components/video_viewer.dart create mode 100644 lib/Components/video_viewer_youtube.dart diff --git a/lib/Components/rounded_password_field.dart b/lib/Components/rounded_password_field.dart index 469ce1c..341eca9 100644 --- a/lib/Components/rounded_password_field.dart +++ b/lib/Components/rounded_password_field.dart @@ -30,7 +30,7 @@ class _RoundedPasswordFieldState extends State { style: TextStyle(fontSize: 20, color: kBlack), decoration: InputDecoration( hintText: "Mot de passe", - hintStyle: TextStyle(fontSize: 20.0, color: kBlack), + hintStyle: TextStyle(fontSize: 20.0, color: kBlack, fontWeight: FontWeight.normal), icon: Icon( Icons.lock, color: kPrimaryColor, diff --git a/lib/Components/video_viewer.dart b/lib/Components/video_viewer.dart new file mode 100644 index 0000000..25da555 --- /dev/null +++ b/lib/Components/video_viewer.dart @@ -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 { + 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), + ), + ) + ], + ); + } +} + diff --git a/lib/Components/video_viewer_youtube.dart b/lib/Components/video_viewer_youtube.dart new file mode 100644 index 0000000..f39e1ca --- /dev/null +++ b/lib/Components/video_viewer_youtube.dart @@ -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 { + 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))); +} \ No newline at end of file diff --git a/lib/Screens/Configurations/Section/SubSection/Puzzle/puzzle_config.dart b/lib/Screens/Configurations/Section/SubSection/Puzzle/puzzle_config.dart index df758e6..9caeb1b 100644 --- a/lib/Screens/Configurations/Section/SubSection/Puzzle/puzzle_config.dart +++ b/lib/Screens/Configurations/Section/SubSection/Puzzle/puzzle_config.dart @@ -1,7 +1,9 @@ import 'package:flutter/cupertino.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_container.dart'; +import 'package:manager_app/Components/number_input_container.dart'; import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_api_new/api.dart'; import 'dart:convert'; @@ -35,70 +37,125 @@ class _PuzzleConfigState extends State { test.image = PuzzleDTOImage(); } puzzleDTO = test; + puzzleDTO.rows = puzzleDTO.rows == null ? 3 : puzzleDTO.rows; + puzzleDTO.cols = puzzleDTO.cols == null ? 3 : puzzleDTO.cols; + super.initState(); } @override Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ResourceInputContainer( - label: "Image du puzzle :", - initialValue: puzzleDTO.image!.resourceId == null ? '': puzzleDTO.image!.resourceId, - onChanged: (ResourceDTO resourceDTO) { - setState(() { - puzzleDTO.image!.resourceId = resourceDTO.id; - puzzleDTO.image!.resourceType = resourceDTO.type; - puzzleDTO.image!.resourceUrl = resourceDTO.url; - 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) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ResourceInputContainer( + label: "Image du puzzle :", + initialValue: puzzleDTO.image!.resourceId == null ? '': puzzleDTO.image!.resourceId, + onChanged: (ResourceDTO resourceDTO) { 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()); }); } - }, - maxLines: 1, - 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 - ) - ), - ], + ), + 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(() { + print(puzzleDTO.messageDebut); + puzzleDTO.messageDebut = value; + widget.onChanged(jsonEncode(puzzleDTO).toString()); + }); + } + }, + maxLines: 1, + 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); + } + }, + ), + ), + ],) + ], + ), ); } } diff --git a/lib/Screens/Configurations/Section/section_detail_screen.dart b/lib/Screens/Configurations/Section/section_detail_screen.dart index f5a5afe..0525ad8 100644 --- a/lib/Screens/Configurations/Section/section_detail_screen.dart +++ b/lib/Screens/Configurations/Section/section_detail_screen.dart @@ -206,14 +206,14 @@ class _SectionDetailScreenState extends State { }); }, ), - if(sectionDTO!.isBeacon!) + if(sectionDTO.isBeacon!) NumberInputContainer( label: "Identifiant Beacon :", - initialValue: sectionDTO!.beaconId != null ? sectionDTO.beaconId! : 0, + initialValue: sectionDTO.beaconId != null ? sectionDTO.beaconId! : 0, isSmall: true, onChanged: (value) { try { - sectionDTO!.beaconId = int.parse(value); + sectionDTO.beaconId = int.parse(value); } catch (e) { print('BeaconId not a number'); showNotification(Colors.orange, kWhite, 'Cela doit être un chiffre', context, null); diff --git a/lib/Screens/Resources/get_element_for_resource.dart b/lib/Screens/Resources/get_element_for_resource.dart index fdb7f71..e9ef3e0 100644 --- a/lib/Screens/Resources/get_element_for_resource.dart +++ b/lib/Screens/Resources/get_element_for_resource.dart @@ -3,6 +3,8 @@ import 'dart:typed_data'; import 'package:manager_app/Components/audio_player.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/app_context.dart'; import 'package:manager_app/constants.dart'; @@ -84,11 +86,25 @@ getElementForResource(dynamic resourceDTO, AppContext appContext) { );*/ //return Text("Fichier audio - aucune visualisation possible"); 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: - 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: return Text("Fichier pdf - aucune visualisation possible"); + case ResourceType.Json: return Text("Fichier json - aucune visualisation possible"); } diff --git a/lib/api/swagger.yaml b/lib/api/swagger.yaml index e6997e0..f8d3bde 100644 --- a/lib/api/swagger.yaml +++ b/lib/api/swagger.yaml @@ -5,7 +5,7 @@ info: description: API Manager Service version: Version Alpha servers: - - url: http://localhost:5000 + - url: https://api.myinfomate.be paths: /api/Configuration: get: @@ -2400,6 +2400,12 @@ components: nullable: true oneOf: - $ref: '#/components/schemas/ContentDTO' + rows: + type: integer + format: int32 + cols: + type: integer + format: int32 AgendaDTO: type: object additionalProperties: false diff --git a/manager_api_new/README.md b/manager_api_new/README.md index a9315b2..65d8acd 100644 --- a/manager_api_new/README.md +++ b/manager_api_new/README.md @@ -60,7 +60,7 @@ try { ## 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 ------------ | ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/AuthenticationApi.md b/manager_api_new/doc/AuthenticationApi.md index b17a2f1..202d6b8 100644 --- a/manager_api_new/doc/AuthenticationApi.md +++ b/manager_api_new/doc/AuthenticationApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/ConfigurationApi.md b/manager_api_new/doc/ConfigurationApi.md index 0b3b922..d790375 100644 --- a/manager_api_new/doc/ConfigurationApi.md +++ b/manager_api_new/doc/ConfigurationApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/DeviceApi.md b/manager_api_new/doc/DeviceApi.md index a586e58..f9b4be4 100644 --- a/manager_api_new/doc/DeviceApi.md +++ b/manager_api_new/doc/DeviceApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/InstanceApi.md b/manager_api_new/doc/InstanceApi.md index 534cad6..87ff28f 100644 --- a/manager_api_new/doc/InstanceApi.md +++ b/manager_api_new/doc/InstanceApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/PuzzleDTO.md b/manager_api_new/doc/PuzzleDTO.md index 3e290e8..0cc61a0 100644 --- a/manager_api_new/doc/PuzzleDTO.md +++ b/manager_api_new/doc/PuzzleDTO.md @@ -11,6 +11,8 @@ Name | Type | Description | Notes **messageDebut** | [**List**](TranslationAndResourceDTO.md) | | [optional] [default to const []] **messageFin** | [**List**](TranslationAndResourceDTO.md) | | [optional] [default to const []] **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) diff --git a/manager_api_new/doc/ResourceApi.md b/manager_api_new/doc/ResourceApi.md index fcf1ea2..2b4b336 100644 --- a/manager_api_new/doc/ResourceApi.md +++ b/manager_api_new/doc/ResourceApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/SectionApi.md b/manager_api_new/doc/SectionApi.md index 7df327e..1d77bc1 100644 --- a/manager_api_new/doc/SectionApi.md +++ b/manager_api_new/doc/SectionApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/doc/UserApi.md b/manager_api_new/doc/UserApi.md index 29e98ed..49c4b9e 100644 --- a/manager_api_new/doc/UserApi.md +++ b/manager_api_new/doc/UserApi.md @@ -5,7 +5,7 @@ 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 ------------- | ------------- | ------------- diff --git a/manager_api_new/lib/api_client.dart b/manager_api_new/lib/api_client.dart index 0193f94..fa3fc94 100644 --- a/manager_api_new/lib/api_client.dart +++ b/manager_api_new/lib/api_client.dart @@ -11,7 +11,7 @@ part of openapi.api; class ApiClient { - ApiClient({this.basePath = 'http://localhost:5000', this.authentication,}); + ApiClient({this.basePath = 'https://api.myinfomate.be', this.authentication,}); final String basePath; final Authentication? authentication; diff --git a/manager_api_new/lib/model/puzzle_dto.dart b/manager_api_new/lib/model/puzzle_dto.dart index 2955a13..14654e5 100644 --- a/manager_api_new/lib/model/puzzle_dto.dart +++ b/manager_api_new/lib/model/puzzle_dto.dart @@ -16,6 +16,8 @@ class PuzzleDTO { this.messageDebut = const [], this.messageFin = const [], this.image, + this.rows, + this.cols, }); List? messageDebut; @@ -24,21 +26,41 @@ class PuzzleDTO { 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 bool operator ==(Object other) => identical(this, other) || other is PuzzleDTO && other.messageDebut == messageDebut && other.messageFin == messageFin && - other.image == image; + other.image == image && + other.rows == rows && + other.cols == cols; @override int get hashCode => // ignore: unnecessary_parenthesis (messageDebut == null ? 0 : messageDebut!.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 - String toString() => 'PuzzleDTO[messageDebut=$messageDebut, messageFin=$messageFin, image=$image]'; + String toString() => 'PuzzleDTO[messageDebut=$messageDebut, messageFin=$messageFin, image=$image, rows=$rows, cols=$cols]'; Map toJson() { final json = {}; @@ -57,6 +79,16 @@ class PuzzleDTO { } else { 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; } @@ -82,6 +114,8 @@ class PuzzleDTO { messageDebut: TranslationAndResourceDTO.listFromJson(json[r'messageDebut']), messageFin: TranslationAndResourceDTO.listFromJson(json[r'messageFin']), image: PuzzleDTOImage.fromJson(json[r'image']), + rows: mapValueOfType(json, r'rows'), + cols: mapValueOfType(json, r'cols'), ); } return null; diff --git a/pubspec.lock b/pubspec.lock index 7089256..f846d3c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1388,6 +1388,22 @@ packages: url: "https://pub.dev" source: hosted 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: dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2ce40d4..91579f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: just_audio: ^0.9.35 pdf: ^3.10.4 multi_select_flutter: ^4.1.3 + youtube_player_iframe: ^4.0.4 #msix: ^2.1.3 #window_size: # git: