From bddde86974a43b21964c9cb16351cb9526016dba Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Wed, 11 Jun 2025 17:26:36 +0200 Subject: [PATCH] Working Web, Pdf and Video type + misc and clean code --- lib/Components/ScannerDialog.dart | 2 - lib/Components/SearchBox.dart | 3 +- lib/Components/SearchNumberBox.dart | 3 +- lib/Screens/Scanner/scanner_old.dart | 2 - .../{ => Sections}/Article/article_page.dart | 2 +- .../{ => Sections}/Article/audio_player.dart | 0 .../Article/audio_player_floating.dart | 0 lib/Screens/Sections/PDF/pdf_filter.dart | 124 +++++++ lib/Screens/Sections/PDF/pdf_view.dart | 349 ++++++++++++++++++ .../{Quizz => Sections/Quiz}/drawPath.dart | 0 .../Quiz}/questions_list.dart | 0 .../{Quizz => Sections/Quiz}/quizz_page.dart | 2 +- lib/Screens/Sections/Video/video_view.dart | 263 +++++++++++++ lib/Screens/Sections/Web/web_page.dart | 242 ++++++++++++ lib/Screens/Visit/beaconArticleFound.dart | 2 +- lib/Screens/Visit/components/body.dart | 17 + lib/Screens/Visit/visit.dart | 4 +- lib/Screens/section_page.dart | 24 +- pubspec.lock | 152 ++++++-- pubspec.yaml | 4 + 20 files changed, 1147 insertions(+), 48 deletions(-) rename lib/Screens/{ => Sections}/Article/article_page.dart (99%) rename lib/Screens/{ => Sections}/Article/audio_player.dart (100%) rename lib/Screens/{ => Sections}/Article/audio_player_floating.dart (100%) create mode 100644 lib/Screens/Sections/PDF/pdf_filter.dart create mode 100644 lib/Screens/Sections/PDF/pdf_view.dart rename lib/Screens/{Quizz => Sections/Quiz}/drawPath.dart (100%) rename lib/Screens/{Quizz => Sections/Quiz}/questions_list.dart (100%) rename lib/Screens/{Quizz => Sections/Quiz}/quizz_page.dart (99%) create mode 100644 lib/Screens/Sections/Video/video_view.dart create mode 100644 lib/Screens/Sections/Web/web_page.dart diff --git a/lib/Components/ScannerDialog.dart b/lib/Components/ScannerDialog.dart index 2dff64c..42845a5 100644 --- a/lib/Components/ScannerDialog.dart +++ b/lib/Components/ScannerDialog.dart @@ -4,8 +4,6 @@ import 'package:flutter/foundation.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; -import 'package:mymuseum_visitapp/Screens/Quizz/quizz_page.dart'; import 'package:mymuseum_visitapp/Screens/section_page.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; diff --git a/lib/Components/SearchBox.dart b/lib/Components/SearchBox.dart index 991918f..d81b3d4 100644 --- a/lib/Components/SearchBox.dart +++ b/lib/Components/SearchBox.dart @@ -32,10 +32,11 @@ class _SearchBoxState extends State { vertical: kDefaultPadding / 4, // 5 top and bottom ), decoration: BoxDecoration( - color: Colors.white.withOpacity(0.4), + color: Colors.white.withValues(alpha: 0.4), borderRadius: BorderRadius.circular(12), ), child: TextFormField( + cursorColor: kMainColor, controller: _controller, onChanged: widget.onChanged, style: const TextStyle(color: Colors.white), diff --git a/lib/Components/SearchNumberBox.dart b/lib/Components/SearchNumberBox.dart index 547a2d6..171570c 100644 --- a/lib/Components/SearchNumberBox.dart +++ b/lib/Components/SearchNumberBox.dart @@ -26,10 +26,11 @@ class _SearchNumberBoxState extends State { vertical: kDefaultPadding / 4, // 5 top and bottom ), decoration: BoxDecoration( - color: Colors.white.withOpacity(0.4), + color: Colors.white.withValues(alpha: 0.4), borderRadius: BorderRadius.circular(12), ), child: TextFormField( + cursorColor: kMainColor, controller: _controller, onChanged: widget.onChanged, keyboardType: TextInputType.number, diff --git a/lib/Screens/Scanner/scanner_old.dart b/lib/Screens/Scanner/scanner_old.dart index a5350a7..2d7bd87 100644 --- a/lib/Screens/Scanner/scanner_old.dart +++ b/lib/Screens/Scanner/scanner_old.dart @@ -6,9 +6,7 @@ import 'package:flutter/material.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; import 'package:mymuseum_visitapp/Screens/section_page.dart'; -import 'package:mymuseum_visitapp/app_context.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; // NOT USED ANYMORE diff --git a/lib/Screens/Article/article_page.dart b/lib/Screens/Sections/Article/article_page.dart similarity index 99% rename from lib/Screens/Article/article_page.dart rename to lib/Screens/Sections/Article/article_page.dart index bb6a040..7ccbfd1 100644 --- a/lib/Screens/Article/article_page.dart +++ b/lib/Screens/Sections/Article/article_page.dart @@ -13,7 +13,7 @@ import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/articleRead.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/audio_player.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Article/audio_player.dart'; import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; diff --git a/lib/Screens/Article/audio_player.dart b/lib/Screens/Sections/Article/audio_player.dart similarity index 100% rename from lib/Screens/Article/audio_player.dart rename to lib/Screens/Sections/Article/audio_player.dart diff --git a/lib/Screens/Article/audio_player_floating.dart b/lib/Screens/Sections/Article/audio_player_floating.dart similarity index 100% rename from lib/Screens/Article/audio_player_floating.dart rename to lib/Screens/Sections/Article/audio_player_floating.dart diff --git a/lib/Screens/Sections/PDF/pdf_filter.dart b/lib/Screens/Sections/PDF/pdf_filter.dart new file mode 100644 index 0000000..ec6d444 --- /dev/null +++ b/lib/Screens/Sections/PDF/pdf_filter.dart @@ -0,0 +1,124 @@ +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/Models/visitContext.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; +import 'package:mymuseum_visitapp/constants.dart'; +import 'package:provider/provider.dart'; + +class PdfFilter extends StatefulWidget { + final List pdfsList; + final Function(int?) onPDFSelected; + + PdfFilter({required this.pdfsList, required this.onPDFSelected}); + + @override + _PdfFilterState createState() => _PdfFilterState(); +} + +class _PdfFilterState extends State with SingleTickerProviderStateMixin { + bool isExpanded = false; + int _selectedOrderPdf = 0; + late AnimationController _controller; + late Animation _widthAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300)); + _widthAnimation = Tween(begin: 40, end: 250).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); + } + + void toggleExpand() { + setState(() { + isExpanded = !isExpanded; + isExpanded ? _controller.forward() : _controller.reverse(); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + VisitAppContext visitAppContext = appContext.getContext(); + var currentLanguage = visitAppContext.language; + + var primaryColor = visitAppContext.configuration?.primaryColor != null + ? Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) + : kSecondColor; + + double rounded = visitAppContext.configuration?.roundedValue?.toDouble() ?? 20.0; + + return AnimatedBuilder( + animation: _widthAnimation, + builder: (context, child) { + return Container( + width: _widthAnimation.value, + height: isExpanded ? 300 : 75, + decoration: BoxDecoration( + color: isExpanded ? primaryColor.withValues(alpha: 0.9) : primaryColor.withValues(alpha: 0.5) , + borderRadius: BorderRadius.only( + topRight: Radius.circular(rounded), + bottomRight: Radius.circular(rounded), + ), + ), + child: isExpanded + ? Column( + children: [ + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: toggleExpand, + ), + Expanded( + child: ListView.builder( + itemCount: widget.pdfsList.length, + itemBuilder: (context, index) { + var pdfItem = widget.pdfsList[index]; + var title = pdfItem.translationAndResourceDTOs! + .firstWhere((pfat) => pfat.language == currentLanguage) + .value; + + bool isSelected = _selectedOrderPdf == pdfItem.order; + + return ListTile( + title: HtmlWidget( + title!, + textStyle: TextStyle( + fontSize: 15.0, + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + color: isSelected ? Colors.white : Colors.black, + fontFamily: 'Roboto', + ), + customStylesBuilder: (element) + { + return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; + }, + ), + onTap: () { + setState(() { + _selectedOrderPdf = pdfItem.order!; + }); + widget.onPDFSelected(_selectedOrderPdf); + toggleExpand(); // collapse after selection + }, + tileColor: isSelected ? primaryColor.withValues(alpha: 0.6) : null, + ); + }, + ), + ), + ], + ) + : IconButton( + icon: const Icon(Icons.menu, color: Colors.white), + onPressed: toggleExpand, + ), + ); + }, + ); + } +} diff --git a/lib/Screens/Sections/PDF/pdf_view.dart b/lib/Screens/Sections/PDF/pdf_view.dart new file mode 100644 index 0000000..82c4b45 --- /dev/null +++ b/lib/Screens/Sections/PDF/pdf_view.dart @@ -0,0 +1,349 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +//import 'dart:html'; +import 'dart:ui' as ui; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_pdfview/flutter_pdfview.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/PDF/pdf_filter.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; +import 'package:mymuseum_visitapp/constants.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; + + +class PDFPage extends StatefulWidget { + final PdfDTO section; + const PDFPage({super.key, required this.section}); + + @override + _PDFPage createState() => _PDFPage(); +} + +class _PDFPage extends State { + PdfDTO pdfDTO = PdfDTO(); + String remotePDFpath = ""; + final Completer _controller = Completer(); + int? pages = 0; + int? currentPage = 0; + bool isReady = false; + String errorMessage = ''; + late ValueNotifier selectedPdf = ValueNotifier(pdfDTO.pdfs!.first); + ValueNotifier> currentState = ValueNotifier>({'page': 0, 'total': 1}); + + @override + void initState() { + /*print(widget.section!.data); + pdfDTO = PdfDTO.fromJson(jsonDecode(widget.section!.data!))!; + print(pdfDTO);*/ + pdfDTO = widget.section; + pdfDTO.pdfs!.sort((a, b) => a.order!.compareTo(b.order!)); + + super.initState(); + + /*createFileOfPdfUrl(pdfDTO.source_!).then((f) { + setState(() { + remotePDFpath = f.path; + print("paaath"); + print(remotePDFpath); + }); + });*/ + } + + Future createFileOfPdfUrl(VisitAppContext visitAppContext, OrderedTranslationAndResourceDTO pdfFileDTO) async { + Completer completer = Completer(); + + if(pdfFileDTO.translationAndResourceDTOs!.firstWhere((pfat) => pfat.language == visitAppContext.language).resourceId == null) { + completer.complete(null); + } else { + var file = await _checkIfLocalResourceExists(visitAppContext, pdfFileDTO.translationAndResourceDTOs!.firstWhere((pfat) => pfat.language == visitAppContext.language).resourceId!); + + if(file == null) { + print("Start download file from internet!"); + try { + // "https://berlin2017.droidcon.cod.newthinking.net/sites/global.droidcon.cod.newthinking.net/files/media/documents/Flutter%20-%2060FPS%20UI%20of%20the%20future%20%20-%20DroidconDE%2017.pdf"; + // final url = "https://pdfkit.org/docs/guide.pdf"; + final url = pdfFileDTO.translationAndResourceDTOs!.firstWhere((pfat) => pfat.language == visitAppContext.language).resource!.url!; + final filename = url.substring(url.lastIndexOf("/") + 1); + var request = await HttpClient().getUrl(Uri.parse(url)); + var response = await request.close(); + var bytes = await consolidateHttpClientResponseBytes(response); + var dir = await getApplicationDocumentsDirectory(); + print("Download files"); + print("${dir.path}/$filename"); + File file = File("${dir.path}/$filename"); + + await file.writeAsBytes(bytes, flush: true); + completer.complete(file); + } catch (e) { + throw Exception('Error parsing asset file!'); + } + } else { + print("FOUND FILE PDF"); + completer.complete(file); + } + } + + return completer.future; + } + + @override + void dispose() { + //_webView = null; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + VisitAppContext visitAppContext = appContext.getContext(); + + var primaryColor = visitAppContext.configuration != null ? visitAppContext.configuration!.primaryColor != null ? new Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : kSecondColor : kSecondColor; + + pdfDTO.pdfs!.sort((a, b) => a.order!.compareTo(b.order!)); + Size size = MediaQuery.of(context).size; + + var title = TranslationHelper.get(widget.section.title, appContext.getContext()); + String cleanedTitle = title.replaceAll('\n', ' ').replaceAll('
', ' '); + + 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: [ + 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: pdfDTO.pdfs != null && pdfDTO.pdfs!.isNotEmpty ? + Center( + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: ValueListenableBuilder( + valueListenable: selectedPdf, + builder: (context, value, _) { + return FutureBuilder( + future: createFileOfPdfUrl(visitAppContext, value!), + builder: (context, AsyncSnapshot snapshot) { + print("snapshot.data"); + print(snapshot.data); + if (snapshot.connectionState == ConnectionState.done) { + if(snapshot.data == null) { + return Center(child: Text("Aucun fichier à afficher")); + } else { + return Stack( + children: [ + PDFView( + filePath: snapshot.data.path, + enableSwipe: true, + fitEachPage: true, + swipeHorizontal: true, + autoSpacing: false, + pageFling: true, + fitPolicy: FitPolicy.HEIGHT, + onRender: (_pages) { + //setState(() { + pages = _pages; + isReady = true; + //}); + }, + onError: (error) { + print(error.toString()); + }, + onPageError: (page, error) { + print('$page: ${error.toString()}'); + }, + onViewCreated: (PDFViewController pdfViewController) { + //_controller.complete(pdfViewController); + }, + onPageChanged: (int? page, int? total) { + currentPage = page; + pages = total; + currentState.value = {'page': page!, 'total': total!}; + + print('page change: $page/$total'); + }, + ), + Positioned( + bottom: 20, + left: 20, + child: ValueListenableBuilder>( + valueListenable: currentState, + builder: (context, value, _) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0)), + color: primaryColor + ), + child: Padding( + padding: const EdgeInsets.all(6.0), + child: Text("${value["page"]!+1}/${value["total"]!}", + style: const TextStyle(color: Colors.white, fontSize: 20)), + ) + ); + }, + ), + ), + ], + ); + } + } else { + return Center( + child: Container( + child: LoadingCommon() + ) + ); + } + } + ); + } + ), + ), + pdfDTO.pdfs!.length > 1 ? Align( + alignment: Alignment.centerLeft, + child: PdfFilter( + pdfsList: pdfDTO.pdfs!, + onPDFSelected: (selectedOrder) { + selectedPdf.value = pdfDTO.pdfs!.firstWhere((pdf) => pdf.order == selectedOrder); + }), + ): const SizedBox(), + ], + ), + ) : + Center(child: Text("Aucun pdf à afficher", style: TextStyle(fontSize: kNoneInfoOrIncorrect)))) + ) + ) + ], + ) + ], + ); + + /**/ + } +} //_webView + +Future _checkIfLocalResourceExists(VisitAppContext visitAppContext, String resourceId) async { + try { + Directory? appDocumentsDirectory = Platform.isIOS ? await getApplicationDocumentsDirectory() : await getDownloadsDirectory(); + String localPath = appDocumentsDirectory!.path; + Directory configurationDirectory = Directory('$localPath/${visitAppContext.configuration!.id}'); + List fileList = configurationDirectory.listSync(); + + if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(resourceId))) { + File file = File(fileList.firstWhere((fileL) => fileL.uri.pathSegments.last.contains(resourceId)).path); + return file; + } + } catch (e) { + print("ERROR _checkIfLocalResourceExists PDF"); + print(e); + } + + return null; +} \ No newline at end of file diff --git a/lib/Screens/Quizz/drawPath.dart b/lib/Screens/Sections/Quiz/drawPath.dart similarity index 100% rename from lib/Screens/Quizz/drawPath.dart rename to lib/Screens/Sections/Quiz/drawPath.dart diff --git a/lib/Screens/Quizz/questions_list.dart b/lib/Screens/Sections/Quiz/questions_list.dart similarity index 100% rename from lib/Screens/Quizz/questions_list.dart rename to lib/Screens/Sections/Quiz/questions_list.dart diff --git a/lib/Screens/Quizz/quizz_page.dart b/lib/Screens/Sections/Quiz/quizz_page.dart similarity index 99% rename from lib/Screens/Quizz/quizz_page.dart rename to lib/Screens/Sections/Quiz/quizz_page.dart index 2915064..2e66d51 100644 --- a/lib/Screens/Quizz/quizz_page.dart +++ b/lib/Screens/Sections/Quiz/quizz_page.dart @@ -15,7 +15,7 @@ import 'package:mymuseum_visitapp/Models/articleRead.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; //import 'package:mymuseum_visitapp/Screens/Quizz/drawPath.dart'; -import 'package:mymuseum_visitapp/Screens/Quizz/questions_list.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Quiz/questions_list.dart'; //import 'package:mymuseum_visitapp/Screens/Quizz/showResponses.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; diff --git a/lib/Screens/Sections/Video/video_view.dart b/lib/Screens/Sections/Video/video_view.dart new file mode 100644 index 0000000..34f3176 --- /dev/null +++ b/lib/Screens/Sections/Video/video_view.dart @@ -0,0 +1,263 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.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/Helpers/translationHelper.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; +import 'package:mymuseum_visitapp/constants.dart'; +import 'package:provider/provider.dart'; +//import 'package:youtube_player_iframe/youtube_player_iframe.dart' as iframe; +import 'package:youtube_player_flutter/youtube_player_flutter.dart'; + +class VideoPage extends StatefulWidget { + final VideoDTO section; + VideoPage({required this.section}); + + @override + _VideoPage createState() => _VideoPage(); +} + +class _VideoPage extends State { + //iframe.YoutubePlayer? _videoViewWeb; + YoutubePlayer? _videoView; + VideoDTO? videoDTO; + late YoutubePlayerController _controller; + bool isFullScreen = false; + + @override + void initState() { + //print(widget.section!.data); + //videoDTO = VideoDTO.fromJson(jsonDecode(widget.section!.data!)); + //print(videoDTO); + videoDTO= widget.section; + + String? videoId; + if (videoDTO!.source_ != null && videoDTO!.source_!.isNotEmpty) { + videoId = YoutubePlayer.convertUrlToId(videoDTO!.source_!); + + /*if (false) { + 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(videoDTO!.source_!); + + _videoViewWeb = iframe.YoutubePlayer( + controller: _controllerWeb, + //showVideoProgressIndicator: false, + /*progressIndicatorColor: Colors.amber, + progressColors: ProgressBarColors( + playedColor: Colors.amber, + handleColor: Colors.amberAccent, + ),*/ + ); + } else {*/ + videoId = YoutubePlayer.convertUrlToId(videoDTO!.source_!); + _controller = YoutubePlayerController( + initialVideoId: videoId!, + flags: const YoutubePlayerFlags( + autoPlay: true, + controlsVisibleAtStart: false, + loop: true, + hideControls: false, + hideThumbnail: true, + ), + )..addListener(_onYoutubePlayerChanged); + + _videoView = YoutubePlayer( + controller: _controller, + //showVideoProgressIndicator: false, + progressIndicatorColor: kMainColor, + progressColors: const ProgressBarColors( + playedColor: kMainColor, + handleColor: kSecondColor, + ), + ); + //} + _controller.toggleFullScreenMode(); + super.initState(); + } + } + + void _onYoutubePlayerChanged() { + if (_controller.value.isFullScreen) { + setState(() { + isFullScreen = true; + }); + } else { + setState(() { + isFullScreen = false; + }); + } + } + + @override + void dispose() { + _videoView = null; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + + var title = TranslationHelper.get(widget.section.title, appContext.getContext()); + String cleanedTitle = title.replaceAll('\n', ' ').replaceAll('
', ' '); + + return Stack( + children: [ + !isFullScreen ? 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, + ), + ) : const SizedBox(), + Column( + children: [ + !isFullScreen ? 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: () { + _controller.dispose(); + _videoView = null; + 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) + ), + ) + ), + ), + ], + ), + ): const SizedBox(), + 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: !isFullScreen ? const BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ): BorderRadius.zero, + child: videoDTO!.source_ != null && videoDTO!.source_!.isNotEmpty ? + _videoView : + const Center(child: Text("La vidéo ne peut pas être affichée, l'url est incorrecte", style: TextStyle(fontSize: kNoneInfoOrIncorrect)))) + ), + ), + ], + ), + isFullScreen ? Positioned( + top: 35, + left: 10, + child: SizedBox( + width: 50, + height: 50, + child: InkWell( + onTap: () { + _controller.toggleFullScreenMode(); + _controller.dispose(); + _videoView = null; + 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) + ), + ) + ), + ): const SizedBox(), + ], + ); + } +} \ No newline at end of file diff --git a/lib/Screens/Sections/Web/web_page.dart b/lib/Screens/Sections/Web/web_page.dart new file mode 100644 index 0000000..7c09925 --- /dev/null +++ b/lib/Screens/Sections/Web/web_page.dart @@ -0,0 +1,242 @@ +import 'dart:convert'; +import 'dart:ui' as ui; + +import 'package:flutter/foundation.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/app_context.dart'; +import 'package:mymuseum_visitapp/constants.dart'; +import 'package:provider/provider.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +class WebPage extends StatefulWidget { + final WebDTO section; + WebPage({required this.section}); + + @override + _WebPage createState() => _WebPage(); +} + +class _WebPage extends State { + //final IFrameElement _iframeElement = IFrameElement(); + //WebView _webView; + WebDTO webDTO = WebDTO(); + WebViewController? controller; + PlatformWebViewController? controllerWeb; + bool isLoading = true; + + @override + void initState() { + //print(widget.section!.data); + webDTO = widget.section; + //webDTO = WebDTO.fromJson(jsonDecode(widget.section!.data!))!; + print(webDTO); + + + if(kIsWeb) { + /*_iframeElement.src = webDTO.source_!; + _iframeElement.style.border = 'none'; + + //ignore: undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory( + webDTO.source_!, //use source as registered key to ensure uniqueness + (int viewId) => _iframeElement, + );*/ + } else { + + if(webDTO.source_ != null && webDTO.source_!.length > 0) { + try { + controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) { + // Update loading bar. + }, + onPageStarted: (String url) {}, + onPageFinished: (String url) { + setState(() { + isLoading = false; + }); + }, + onWebResourceError: (WebResourceError error) {}, + onNavigationRequest: (NavigationRequest request) { + Uri sourceUri = Uri.parse(webDTO.source_!); + Uri requestUri = Uri.parse(request.url); + + if (requestUri.host != sourceUri.host) { // handle navigation to site + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ) + ..loadRequest(Uri.parse(webDTO.source_!)); + } catch (e) { + print("Invalid source ${webDTO.source_}"); + } + } + } + + super.initState(); + /*_webView = WebView( + initialUrl: webDTO.source_, //"https://my.matterport.com/show/?m=k8bvdezfHbT" + javascriptMode: JavascriptMode.unrestricted, + navigationDelegate: (NavigationRequest request) { + print(request.url); + print(webDTO.source_); + if (request.url != webDTO.source_) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + } + );*/ + } + + @override + void dispose() { + //_webView = null; + super.dispose(); + } + + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + + var title = TranslationHelper.get(widget.section.title, appContext.getContext()); + String cleanedTitle = title.replaceAll('\n', ' ').replaceAll('
', ' '); + + return webDTO.source_ != null && webDTO.source_!.isNotEmpty ? + kIsWeb ? + HtmlElementView( + key: UniqueKey(), + viewType: webDTO.source_!, + ) : + 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: [ + 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: isLoading ? Center(child: SizedBox(height: size.height * 0.15, child: const LoadingCommon())) : WebViewWidget(controller: controller!)) + ), + ), + ], + ), + ], + ) : + const Center(child: Text("La page internet ne peut pas être affichée, l'url est incorrecte ou vide", style: TextStyle(fontSize: kNoneInfoOrIncorrect))); + } +} //_webView \ No newline at end of file diff --git a/lib/Screens/Visit/beaconArticleFound.dart b/lib/Screens/Visit/beaconArticleFound.dart index cac1803..8e5a524 100644 --- a/lib/Screens/Visit/beaconArticleFound.dart +++ b/lib/Screens/Visit/beaconArticleFound.dart @@ -12,7 +12,7 @@ import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/beaconSection.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Article/article_page.dart'; import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; diff --git a/lib/Screens/Visit/components/body.dart b/lib/Screens/Visit/components/body.dart index b80c5cd..a307835 100644 --- a/lib/Screens/Visit/components/body.dart +++ b/lib/Screens/Visit/components/body.dart @@ -69,6 +69,22 @@ class _BodyState extends State { offset: Offset(0, 1), // changes position of shadow ), ], + gradient: widget.configuration.imageSource == null ? 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, + + ], + ) : null, image: widget.configuration.imageSource != null ? DecorationImage( fit: BoxFit.cover, opacity: 0.65, @@ -170,6 +186,7 @@ class _BodyState extends State { return Padding( padding: const EdgeInsets.only(bottom: 0), child: RefreshIndicator( + color: kMainColor, onRefresh: () async { if(!widget.configuration.isOffline!) { setState(() { diff --git a/lib/Screens/Visit/visit.dart b/lib/Screens/Visit/visit.dart index 12def0f..32ca32d 100644 --- a/lib/Screens/Visit/visit.dart +++ b/lib/Screens/Visit/visit.dart @@ -13,8 +13,8 @@ import 'package:mymuseum_visitapp/Helpers/requirement_state_controller.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/beaconSection.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; -import 'package:mymuseum_visitapp/Screens/Quizz/quizz_page.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Article/article_page.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Quiz/quizz_page.dart'; import 'package:mymuseum_visitapp/Screens/Visit/beaconArticleFound.dart'; import 'package:mymuseum_visitapp/Screens/section_page.dart'; import 'package:mymuseum_visitapp/app_context.dart'; diff --git a/lib/Screens/section_page.dart b/lib/Screens/section_page.dart index 4ec8c3a..dda86e7 100644 --- a/lib/Screens/section_page.dart +++ b/lib/Screens/section_page.dart @@ -7,20 +7,17 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; -import 'package:mymuseum_visitapp/Components/Loading.dart'; import 'package:mymuseum_visitapp/Components/loading_common.dart'; -import 'package:mymuseum_visitapp/Components/rounded_button.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; -import 'package:mymuseum_visitapp/Models/ResponseSubDTO.dart'; import 'package:mymuseum_visitapp/Models/articleRead.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; -//import 'package:mymuseum_visitapp/Screens/Quizz/drawPath.dart'; -import 'package:mymuseum_visitapp/Screens/Quizz/questions_list.dart'; -import 'package:mymuseum_visitapp/Screens/Quizz/quizz_page.dart'; -//import 'package:mymuseum_visitapp/Screens/Quizz/showResponses.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/Quiz/quizz_page.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Video/video_view.dart'; +import 'package:mymuseum_visitapp/Screens/Sections/Web/web_page.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/constants.dart'; @@ -67,7 +64,7 @@ class _SectionPageState extends State { return Scaffold( key: _scaffoldKey, - appBar: test!.type != SectionType.Quiz && test.type != SectionType.Article ? CustomAppBar( + appBar: test!.type != SectionType.Quiz && test.type != SectionType.Article && test.type != SectionType.Web && test.type != SectionType.Pdf && test.type != SectionType.Video ? CustomAppBar( title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "", isHomeButton: false, ) : null, @@ -85,6 +82,15 @@ class _SectionPageState extends State { case SectionType.Quiz: QuizDTO quizDTO = QuizDTO.fromJson(sectionResult)!; return QuizPage(visitAppContextIn: widget.visitAppContextIn, quizDTO: quizDTO, resourcesModel: resourcesModel); + case SectionType.Web: + WebDTO webDTO = WebDTO.fromJson(sectionResult)!; + return WebPage(section: webDTO); + case SectionType.Pdf: + PdfDTO pdfDTO = PdfDTO.fromJson(sectionResult)!; + return PDFPage(section: pdfDTO); + case SectionType.Video: + VideoDTO videoDTO = VideoDTO.fromJson(sectionResult)!; + return VideoPage(section: videoDTO); default: return const Center(child: Text("Unsupported type")); } diff --git a/pubspec.lock b/pubspec.lock index 577d188..0c00b40 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -318,6 +318,70 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.2" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" flutter_lints: dependency: "direct dev" description: @@ -331,6 +395,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_pdfview: + dependency: "direct main" + description: + name: flutter_pdfview + sha256: "51413e36ab3f1a2fe0edf97ebfa770e20182ea4a066bc9f292920330d9245c9d" + url: "https://pub.dev" + source: hosted + version: "1.4.0+1" flutter_staggered_grid_view: dependency: "direct main" description: @@ -377,10 +449,10 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: "7eae679e596a44fdf761853a706f74979f8dd3cd92cf4e23cae161fda091b847" + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" url: "https://pub.dev" source: hosted - version: "8.2.6" + version: "8.2.12" frontend_server_client: dependency: transitive description: @@ -521,10 +593,10 @@ packages: dependency: transitive description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.4.0" http_multi_server: dependency: transitive description: @@ -601,10 +673,10 @@ packages: dependency: transitive description: name: just_audio_web - sha256: "0edb481ad4aa1ff38f8c40f1a3576013c3420bf6669b686fe661627d49bc606c" + sha256: "9a98035b8b24b40749507687520ec5ab404e291d2b0937823ff45d92cb18d448" url: "https://pub.dev" source: hosted - version: "0.4.11" + version: "0.4.13" leak_tracker: dependency: transitive description: @@ -736,18 +808,18 @@ packages: dependency: transitive description: name: package_info_plus - sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0 + sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "8.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.2.0" path: dependency: transitive description: @@ -976,10 +1048,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: @@ -1125,10 +1197,10 @@ packages: dependency: transitive description: name: url_launcher - sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.3.1" url_launcher_android: dependency: transitive description: @@ -1173,10 +1245,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -1261,10 +1333,10 @@ packages: dependency: transitive description: name: video_player_web - sha256: ff4d69a6614b03f055397c27a71c9d3ddea2b2a23d71b2ba0164f59ca32b8fe2 + sha256: "881b375a934d8ebf868c7fb1423b2bfaa393a0a265fa3f733079a86536064a10" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.3" vm_service: dependency: transitive description: @@ -1277,18 +1349,18 @@ packages: dependency: transitive description: name: wakelock_plus - sha256: "14758533319a462ffb5aa3b7ddb198e59b29ac3b02da14173a1715d65d4e6e68" + sha256: "775c50f226ab43ff859b479acc73f11c0744bf345a782e83355c4d25df758803" url: "https://pub.dev" source: hosted - version: "1.2.5" + version: "1.3.0" wakelock_plus_platform_interface: dependency: transitive description: name: wakelock_plus_platform_interface - sha256: "422d1cdbb448079a8a62a5a770b69baa489f8f7ca21aef47800c726d404f9d16" + sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.3" watcher: dependency: transitive description: @@ -1301,26 +1373,26 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.1" web_socket: dependency: transitive description: name: web_socket - sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078" + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.3" webview_flutter: dependency: "direct main" description: @@ -1385,6 +1457,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + youtube_player_flutter: + dependency: "direct main" + description: + name: youtube_player_flutter + sha256: "4d14aa47f9c84929b5400a87ade4dcfdab87a2ca2e0b18ecc2ef852b1440e123" + url: "https://pub.dev" + source: hosted + version: "9.1.1" + youtube_player_iframe: + dependency: "direct main" + description: + name: youtube_player_iframe + sha256: "66020f7756accfb22b3297565d845f9bef14249c730dd51e1ec648fa155fb24a" + url: "https://pub.dev" + source: hosted + version: "5.2.1" + youtube_player_iframe_web: + dependency: transitive + description: + name: youtube_player_iframe_web + sha256: "05222a228937932e7ee7a6171e8020fee4cd23d1c7bf6b4128c569484338c593" + url: "https://pub.dev" + source: hosted + version: "3.1.1" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 97501c4..b3fd34a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,10 @@ dependencies: webview_flutter: ^4.10.0 ar_flutter_plugin: ^0.7.3 + flutter_pdfview: ^1.4.0+1 + youtube_player_flutter: ^9.1.1 + youtube_player_iframe: ^5.2.1 + # Specific mobile qr_code_scanner: ^1.0.1 #not in web sqflite: #not in web