import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:manager_api_new/api.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/Components/loading_common.dart'; import 'package:tablet_app/Helpers/DatabaseHelper.dart'; import 'package:tablet_app/Helpers/ImageCustomProvider.dart'; import 'package:tablet_app/Helpers/MQTTHelper.dart'; import 'package:tablet_app/Helpers/translationHelper.dart'; import 'package:tablet_app/Screens/Agenda/agenda_view.dart'; import 'package:tablet_app/Screens/Article/article_view.dart'; import 'package:tablet_app/Screens/Configuration/config_view.dart'; import 'package:tablet_app/Screens/MainView/section_page_detail.dart'; import 'package:tablet_app/Screens/Weather/weather_view.dart'; import 'package:tablet_app/Screens/Map/map_context.dart'; import 'package:tablet_app/Screens/Map/map_view.dart'; import 'package:tablet_app/Models/tabletContext.dart'; import 'package:tablet_app/Screens/Menu/menu_view.dart'; import 'package:tablet_app/Screens/PDF/pdf_view.dart'; import 'package:tablet_app/Screens/Puzzle/puzzle_view.dart'; import 'package:tablet_app/Screens/Slider/slider_view.dart'; import 'package:tablet_app/Screens/Video/video_view.dart'; import 'package:tablet_app/Screens/Web/web_view.dart'; import 'package:tablet_app/Services/downloadService.dart'; import 'package:tablet_app/app_context.dart'; import 'package:tablet_app/constants.dart'; import 'package:intl/intl.dart'; import 'package:http/http.dart' as http; import 'package:image/image.dart' as IMG; import 'dart:ui' as ui; import '../Quizz/quizz_view.dart'; import 'language_selection.dart'; class MainViewWidget extends StatefulWidget { MainViewWidget(); @override _MainViewWidget createState() => _MainViewWidget(); } class _MainViewWidget extends State { Size sizeScreen = new Size(1080.0, 1920.0); // Tablet resolution SectionDTO? sectionSelected; late ConfigurationDTO configurationDTO; ValueNotifier currentHourDate = ValueNotifier((DateTime.now().toUtc()).add(Duration(hours: 1))); late Color backgroundColor; int rowCount = 4; bool isImageBackground = false; List? sectionsLocal; late List rawSectionsData; bool isInit = true; // Use to make it faster and to load resource at init bool isDialogOpen = false; @override void initState() { final appContext = Provider.of(context, listen: false); configurationDTO = appContext.getContext().configuration; if(configurationDTO.isHour != null && configurationDTO.isHour!) { Timer.periodic(Duration(seconds: 1), (Timer t) => _getTime(currentHourDate)); } isImageBackground = configurationDTO.isSectionImageBackground ?? true; super.initState(); } void _getTime(ValueNotifier valueNotifier) { //final DateTime now = DateTime.now(); //final String formattedDateTime = _formatDateTime(now); //setState(() { //_timeString = formattedDateTime; valueNotifier.value = (DateTime.now().toUtc()).add(Duration(hours: 2)); //}); } @override Widget build(BuildContext context) { final appContext = Provider.of(context); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); Size size = MediaQuery.of(context).size; backgroundColor = appContext.getContext().configuration != null ? new Color(int.parse(appContext.getContext().configuration.secondaryColor.split('(0x')[1].split(')')[0], radix: 16)) : Colors.white; Color textColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; TabletAppContext tabletAppContext = appContext.getContext() as TabletAppContext; // TODO REMOVE /*if (!MQTTHelper.instance.isInstantiated) MQTTHelper.instance.connect(appContext);*/ return Scaffold( resizeToAvoidBottomInset: false, body: Container( height: size.height, width: size.width, color: configurationDTO.imageId == null ? configurationDTO.secondaryColor != null ? backgroundColor : kBackgroundGrey : null, decoration: configurationDTO.imageId != null ? BoxDecoration( image: new DecorationImage( fit: BoxFit.cover, colorFilter: new ColorFilter.mode(Colors.grey.withOpacity(0.1), BlendMode.color), image: ImageCustomProvider.getImageProvider(appContext, configurationDTO.imageId!, configurationDTO.imageSource!) ), ) : null, child: Stack( children: [ if(currentHourDate.value != null) ValueListenableBuilder( valueListenable: currentHourDate, builder: (context, value, _) { return Positioned( top: 0, left: 0, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ if(value != null && configurationDTO.isHour!) Text(DateFormat('HH:mm').format(value), style: TextStyle(fontSize: 20, color: textColor)), if(value != null && configurationDTO.isDate!) if(appContext.getContext().language.toString().toUpperCase() == "EN") Text(DateFormat('MM/dd').format(value), style: TextStyle(fontSize: 15, color: textColor)), if(value != null && configurationDTO.isDate!) if(appContext.getContext().language.toString().toUpperCase() != "EN") Text(DateFormat('dd/MM').format(value), style: TextStyle(fontSize: 15, color: textColor)), ], ), ) ); }, ), LanguageSelection(size: size), Center( child: Container( height: configurationDTO.screenPercentageSectionsMainPage != null ? size.height * (configurationDTO.screenPercentageSectionsMainPage! / 100) : size.height * 0.85, width: size.width * 0.9, child: isInit ? FutureBuilder( future: getSections(size, appContext), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data == null) { // Show select config return Text(""); } else { return getGridSections(appContext, textColor); } } else if (snapshot.connectionState == ConnectionState.none) { return Text("No data"); } else { return Center( child: Container( child: LoadingCommon() ) ); } } ) : getGridSections(appContext, textColor), ), ), Positioned( bottom: 0, right: 0, child: Padding( padding: const EdgeInsets.all(4.0), child: FutureBuilder( future: getPlatformInfo(), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { return Text(snapshot.data.version, style: TextStyle(fontSize: 8)); } return SizedBox(); } ), ) ) /*if(configurationDTO.weatherCity != null && configurationDTO.weatherCity!.length > 2 && configurationDTO.weatherResult != null) Positioned( bottom: 0, left: 0, child: Padding( padding: const EdgeInsets.all(5.0), child: Builder( builder: (context) { Map weatherResultInJson = jsonDecode(configurationDTO.weatherResult!); WeatherData weatherDataResult = WeatherData.fromJson(weatherResultInJson); return WeatherView(weatherData: weatherDataResult); } ), ))*/ ]), ), ); } Future getCurrentConfiguration(dynamic appContext) async { TabletAppContext tabletAppContext = await appContext.getContext(); try { ConfigurationDTO? configurationDTO = await tabletAppContext.clientAPI!.configurationApi!.configurationGetDetail(tabletAppContext.configuration!.id!); // tabletAppContext.configuration!.id! // 65c5f0ee4c030e63ce16bff5 TODO Remove tabletAppContext.configuration = configurationDTO; backgroundColor = tabletAppContext.configuration != null ? new Color(int.parse(appContext.getContext().configuration.secondaryColor.split('(0x')[1].split(')')[0], radix: 16)) : Colors.white; TabletAppContext? localContext = await DatabaseHelper.instance.getData(); if (localContext != null) { // Check if sql DB exist await DatabaseHelper.instance.update(tabletAppContext); } else { await DatabaseHelper.instance.insert(tabletAppContext); } /* appContext.setContext(tabletAppContext); // STORE IT LOCALLY (SQLite) TabletAppContext? localContext = await DatabaseHelper.instance.getData(); if (localContext != null) { // Check if sql DB exist await DatabaseHelper.instance.update(tabletAppContext); } else { await DatabaseHelper.instance.insert(tabletAppContext); }*/ return configurationDTO; } catch (e) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Une erreur est survenue lors de la récupération de la configuration'), ), ); } } Future?> getSections(Size size, dynamic appContext) async { TabletAppContext tabletAppContext = await appContext.getContext(); // DIALOG HERE if(!isDialogOpen) { isDialogOpen = true; var result = await showDialog( builder: (BuildContext dialogContext) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(tabletAppContext.configuration!.roundedValue?.toDouble() ?? 20.0)) ), content: Container( width: 400, height: 200, child: DownloadConfigurationWidget(), ), actions: [], ), context: context ); isDialogOpen = false; } else { print("ALREADY OPEN LAAAA"); } await getCurrentConfiguration(appContext); print(sectionsLocal); if(isInit) { try { final rawList = await tabletAppContext.clientAPI!.sectionApi!.sectionGetFromConfigurationDetail(tabletAppContext.configuration!.id!); //var sections = rawList.map((json) => SectionDTO.fromJson(json)).toList(); rawSectionsData = jsonDecode(jsonEncode(rawList)); rawSectionsData = rawSectionsData.map((json) => SectionDTO.fromJson(json)).toList(); List sectionList = rawSectionsData.whereType().toList(); sectionList.sort((a, b) => a.order!.compareTo(b.order!)); /*List? sections = await tabletAppContext.clientAPI!.sectionApi!.sectionGetFromConfiguration(tabletAppContext.configuration!.id!); sections!.sort((a, b) => a.order!.compareTo(b.order!));*/ sectionsLocal = sectionList; isInit = false; return sectionList; } catch (e) { print(e); print("IN CATCH"); Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) { return ConfigViewWidget(); }, ), (Route route) => false // For pushAndRemoveUntil ); } } return sectionsLocal; } getGridSections(AppContext appContext, Color textColor) { TabletAppContext tabletAppContext = appContext.getContext() as TabletAppContext; if(sectionsLocal != null) { var isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; return Center( child: Container( //color: Colors.greenAccent, child: GridView.builder( shrinkWrap: true, gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: rowCount), itemCount: sectionsLocal!.length, itemBuilder: (BuildContext context, int index) { return InkWell( onTap: () { setState(() { //sectionSelected = sectionsLocal![index]; Navigator.push( context, MaterialPageRoute( builder: (context) { return SectionPageDetail(configurationDTO: configurationDTO, sectionDTO: sectionsLocal![index], textColor: textColor, isImageBackground: isImageBackground, elementToShow: getContent(tabletAppContext, sectionsLocal![index], isImageBackground, rawSectionsData[index]), isFromMenu: false); }, ),// For pushAndRemoveUntil ); }); }, child: Container( decoration: isImageBackground ? boxDecoration(appContext, sectionsLocal![index], false) : null, //color: Colors.yellow, padding: EdgeInsets.all(isImageBackground ? 18 : 5), margin: EdgeInsets.symmetric(vertical: isImageBackground ? 25 : 10, horizontal: 25), child: isImageBackground ? Align( alignment: Alignment.bottomRight, child: FractionallySizedBox( heightFactor: 0.5, child: Container( //color: Colors.green, child: SizedBox( width: double.infinity, child: Align( alignment: Alignment.centerRight, child: HtmlWidget( sectionsLocal![index].title!.firstWhere((translation) => translation.language == appContext.getContext().language).value ?? "", customStylesBuilder: (element) { return {'text-align': 'right', 'font-family': "Roboto"}; }, textStyle: TextStyle(fontSize: isPortrait ? 10 : 25),//calculateFontSize(constraints.maxWidth, constraints.maxHeight, kIsWeb ? kWebMenuTitleDetailSize : kMenuTitleDetailSize)), ), ), ), ) ), ) : Column( children: [ Expanded( flex: 8, child: Container( decoration: BoxDecoration( color: sectionsLocal![index].imageSource == null ? kBackgroundLight : null, // default color if no image shape: BoxShape.rectangle, image: sectionsLocal![index].imageSource != null || sectionsLocal![index].type == SectionType.Video ? new DecorationImage( fit: BoxFit.cover, image: ImageCustomProvider.getImageProvider(appContext, sectionsLocal![index].imageId, sectionsLocal![index].type == SectionType.Video ? getYoutubeThumbnailUrl(sectionsLocal![index]) : sectionsLocal![index].imageSource!), ): null, ), ) ), Expanded( flex: 2, child: Center( child: HtmlWidget( sectionsLocal![index].title!.where((translation) => translation.language == appContext.getContext().language).firstOrNull?.value ?? "", customStylesBuilder: (element) { return {'text-align': 'center', 'font-family': "Roboto"}; }, textStyle: TextStyle(fontSize: isPortrait ? 10 : 25),//calculateFontSize(constraints.maxWidth, constraints.maxHeight, kIsWeb ? kWebMenuTitleDetailSize : kMenuTitleDetailSize)), ), ) ) ], ) ), ); } ), ), ); } else { Center( child: Text("Aucune section à afficher", style: TextStyle(fontSize: kNoneInfoOrIncorrect)), ); } } getPlatformInfo() async { return await PackageInfo.fromPlatform(); } } boxDecoration(AppContext appContext, SectionDTO section, bool isSelected) { TabletAppContext tabletAppContext = appContext.getContext() as TabletAppContext; return BoxDecoration( color: !isSelected ? kBackgroundLight : section.imageSource == null ? kBackgroundLight : null, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(tabletAppContext.configuration!.roundedValue?.toDouble() ?? 30.0), image: section.imageSource != null || section.type == SectionType.Video ? new DecorationImage( fit: BoxFit.cover, colorFilter: !isSelected? new ColorFilter.mode(kBackgroundLight.withOpacity(0.2), BlendMode.dstATop) : null, image: ImageCustomProvider.getImageProvider(appContext, section.imageId, section.type == SectionType.Video ? getYoutubeThumbnailUrl(section) : section.imageSource!), ): null, boxShadow: [ BoxShadow( color: kBackgroundSecondGrey, spreadRadius: 0.5, blurRadius: 5, offset: Offset(0, 1.5), // changes position of shadow ), ], ); } Future>> getByteIcons(TabletAppContext tabletAppContext, MapDTO mapDTO) async { //var mapDTO = MapDTO.fromJson(jsonDecode(section.data!)); var selectedMarkerIcon; if (mapDTO.iconSource != null) { if (kIsWeb) { Uint8List fileData = await http.readBytes(Uri.parse(mapDTO.iconSource!)); selectedMarkerIcon = resizeImage(fileData, 40); } else { File? localIcon = await _checkIfLocalResourceExists(tabletAppContext, mapDTO.iconResourceId!); if(localIcon == null) { final ByteData imageData = await NetworkAssetBundle(Uri.parse(mapDTO.iconSource!)).load(""); selectedMarkerIcon = await getBytesFromAsset(imageData, 50); } else { Uint8List bytes = await localIcon.readAsBytes(); selectedMarkerIcon = await getBytesFromAsset(ByteData.view(bytes.buffer), 50); } } } else { // Icône par défaut final ByteData bytes = await rootBundle.load('assets/icons/marker.png'); selectedMarkerIcon = await getBytesFromAsset(bytes, 25); } List> icons = []; icons.add({'id': null, 'icon': selectedMarkerIcon}); // Utiliser Future.forEach() pour itérer de manière asynchrone sur la liste des catégories await Future.forEach(mapDTO!.categories!, (cat) async { if (cat.resourceDTO != null && cat.resourceDTO!.url != null && cat.resourceDTO!.id != null) { Uint8List categoryIcon; if (kIsWeb) { categoryIcon = await http.readBytes(Uri.parse(cat.resourceDTO!.url!)); } else { File? localIcon = await _checkIfLocalResourceExists(tabletAppContext, cat.resourceDTO!.id!); if(localIcon == null) { final ByteData imageData = await NetworkAssetBundle(Uri.parse(cat.resourceDTO!.url!)).load(""); categoryIcon = await getBytesFromAsset(imageData, 50); } else { Uint8List bytes = await localIcon.readAsBytes(); categoryIcon = await getBytesFromAsset(ByteData.view(bytes.buffer), 50); } } icons.add({'id': cat.id, 'icon': categoryIcon}); } }); return icons; } Future _checkIfLocalResourceExists(TabletAppContext tabletAppContext, String iconId) async { try { Directory? appDocumentsDirectory = Platform.isIOS ? await getApplicationDocumentsDirectory() : await getDownloadsDirectory(); String localPath = appDocumentsDirectory!.path; Directory configurationDirectory = Directory('$localPath/${tabletAppContext.configuration!.id}'); List fileList = configurationDirectory.listSync(); if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(iconId))) { File file = File(fileList.firstWhere((fileL) => fileL.uri.pathSegments.last.contains(iconId)).path); return file; } } catch(e) { print("ERROR _checkIfLocalResourceExists CachedCustomResource"); print(e); } return null; } Future getBytesFromAsset(ByteData data, int width) async { //ByteData data = await rootBundle.load(path); ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width); ui.FrameInfo fi = await codec.getNextFrame(); return (await fi.image.toByteData(format: ui.ImageByteFormat.png))!.buffer.asUint8List(); } Uint8List resizeImage(Uint8List data, int width) { Uint8List resizedData = data; IMG.Image img = IMG.decodeImage(data)!; IMG.Image resized = IMG.copyResize(img, width: width); resizedData = Uint8List.fromList(IMG.encodeJpg(resized)); return resizedData; } Widget getContent(TabletAppContext tabletAppContext, SectionDTO sectionSelected, bool isImageBackground, Object rawSectionData) { switch (sectionSelected.type) { case SectionType.Map : // MAP MapDTO mapDTO = MapDTO.fromJson(rawSectionData)!; return ChangeNotifierProvider( create: (_) => MapContext(null), child: FutureBuilder( future: getByteIcons(tabletAppContext, mapDTO), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { return MapView(section: sectionSelected, icons: snapshot.data); } else if (snapshot.connectionState == ConnectionState.none) { return Text("No data"); } else { return Center( child: Container( child: LoadingCommon() ) ); } } ) /*FutureBuilder( future: _url, builder: (BuildContext context, AsyncSnapshot snapshot) => snapshot.hasData ? WebViewWidget(url: snapshot.data,) : CircularProgressIndicator()),*/ ); case SectionType.Web : // WEB WebDTO webDTO = WebDTO.fromJson(rawSectionData)!; return WebView(section: webDTO); case SectionType.Video : // Video VideoDTO videoDTO = VideoDTO.fromJson(rawSectionData)!; return VideoView(section: videoDTO); case SectionType.Slider : SliderDTO sliderDTO = SliderDTO.fromJson(rawSectionData)!; return SliderView(section: sliderDTO); case SectionType.Menu : MenuDTO menuDTO = MenuDTO.fromJson(rawSectionData)!; return MenuView(section: menuDTO, isImageBackground: isImageBackground); case SectionType.Quiz : QuizDTO quizDTO = QuizDTO.fromJson(rawSectionData)!; return QuizzView(section: quizDTO); case SectionType.Pdf : PdfDTO pdfDTO = PdfDTO.fromJson(rawSectionData)!; return PDFViewWidget(section: pdfDTO); case SectionType.Puzzle : PuzzleDTO puzzleDTO = PuzzleDTO.fromJson(rawSectionData)!; return PuzzleView(section: puzzleDTO); case SectionType.Agenda : AgendaDTO agendaDTO = AgendaDTO.fromJson(rawSectionData)!; return AgendaView(section: agendaDTO); /*case SectionType.article : // TODO elementToShow = ArticleView(section: sectionSelected); break;*/ case SectionType.Weather : WeatherDTO weatherDTO = WeatherDTO.fromJson(rawSectionData)!; return WeatherView(section: weatherDTO); default: return Text("Ce type n'est pas supporté"); }; }