Slider ok, map wip + update gradle etc + qr scanner to mobile scanner

This commit is contained in:
Thomas Fransolet 2025-06-18 18:00:29 +02:00
parent 7aca0638ce
commit 4e9dc59df9
33 changed files with 2866 additions and 445 deletions

View File

@ -39,13 +39,21 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"*/ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"*/
android { android {
compileSdkVersion 34 namespace = "be.unov.mymuseum.fortsaintheribert"
compileSdkVersion 35
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
packagingOptions {
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
}
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }

View File

@ -51,7 +51,8 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyDg6ApuZb6TRsauIyHJ9-XVwGYeh7MsWXE"/>
<meta-data <meta-data
android:name="com.google.firebase.messaging.default_notification_icon" android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/logo" /> android:resource="@drawable/logo" />

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip

View File

@ -31,7 +31,7 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.2.0" apply false id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.0" apply false id "org.jetbrains.kotlin.android" version "1.9.0" apply false
} }

BIN
assets/icons/marker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
import 'package:mymuseum_visitapp/Components/SlideFromRouteRight.dart';
import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Screens/section_page.dart'; import 'package:mymuseum_visitapp/Screens/section_page.dart';
import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/constants.dart'; import 'package:mymuseum_visitapp/constants.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:mobile_scanner/mobile_scanner.dart';
class ScannerDialog extends StatefulWidget { class ScannerDialog extends StatefulWidget {
const ScannerDialog({Key? key, required this.appContext}) : super(key: key); const ScannerDialog({Key? key, required this.appContext}) : super(key: key);
@ -21,19 +21,16 @@ class ScannerDialog extends StatefulWidget {
} }
class _ScannerDialogState extends State<ScannerDialog> { class _ScannerDialogState extends State<ScannerDialog> {
Barcode? result; final MobileScannerController controller = MobileScannerController();
QRViewController? controller; bool isProcessing = false;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
@override @override
void reassemble() { void reassemble() {
super.reassemble(); super.reassemble();
if (Platform.isAndroid) { if (Platform.isAndroid) {
controller!.pauseCamera(); controller.stop();
} }
controller!.resumeCamera(); controller.start();
} }
@override @override
@ -48,187 +45,114 @@ class _ScannerDialogState extends State<ScannerDialog> {
Center( Center(
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
child: _buildQrView(context), child: MobileScanner(
) controller: controller,
), //allowDuplicates: false,
Positioned( onDetect: (barcodes) => _onDetect(barcodes),
top: 0,
right: 0,
child: Container(
width: 45,
height: 45,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: kMainColor1,
borderRadius: BorderRadius.circular(20.0),
),
margin: const EdgeInsets.all(8),
child: InkWell(
onTap: () async {
await controller?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: controller?.getFlashStatus(),
builder: (context, snapshot) {
return const Icon(Icons.flash_on, color: Colors.white);
},
)),
), ),
), ),
Positioned(
bottom: 0,
right: 0,
child: Container(
width: 45,
height: 45,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: kMainColor1,
borderRadius: BorderRadius.circular(20.0),
), ),
margin: const EdgeInsets.all(8), _buildControlButton(
child: InkWell( icon: Icons.flash_on,
onTap: () async { onTap: () => controller.toggleTorch(),
await controller?.flipCamera(); alignment: Alignment.topRight,
setState(() {});
},
child: FutureBuilder(
future: controller?.getCameraInfo(),
builder: (context, snapshot) {
return const Icon(Icons.flip_camera_android, color: Colors.white);
},
)),
), ),
_buildControlButton(
icon: Icons.flip_camera_android,
onTap: () => controller.switchCamera(),
alignment: Alignment.bottomRight,
), ),
], ],
), ),
); );
} }
Widget _buildQrView(BuildContext context) { Widget _buildControlButton({
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly. required IconData icon,
var scanArea = (MediaQuery.of(context).size.width < 400 || required VoidCallback onTap,
MediaQuery.of(context).size.height < 400) required Alignment alignment,
? 225.0 }) {
: 300.0; return Align(
alignment: alignment,
// To ensure the Scanner view is properly sizes after rotation child: Container(
// we need to listen for Flutter SizeChanged notification and update controller width: 45,
return QRView( height: 45,
key: qrKey, margin: const EdgeInsets.all(8),
onQRViewCreated: _onQRViewCreated, decoration: BoxDecoration(
overlay: QrScannerOverlayShape( shape: BoxShape.rectangle,
borderColor: kMainColor1, color: kMainColor1,
borderRadius: 10, borderRadius: BorderRadius.circular(20.0),
borderLength: 25, ),
borderWidth: 5, child: InkWell(
overlayColor: Colors.black.withValues(alpha: 0.55), onTap: onTap,
cutOutSize: 225.0), child: Icon(icon, color: Colors.white),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p), ),
);
}
_onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
if (Platform.isAndroid) {
controller.pauseCamera();
}
controller.resumeCamera();
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
var code = result == null ? "" : result!.code.toString();
if(result!.format == BarcodeFormat.qrcode) {
controller.pauseCamera();
RegExp regExp = RegExp(r'^(?:https:\/\/web\.myinfomate\.be\/([^\/]+)\/([^\/]+)\/([^\/]+)|([^\/]+))$');
var match = regExp.firstMatch(code);
String? instanceId;
String? configurationId;
String? sectionId;
if (match != null) {
instanceId = match.group(1);
configurationId = match.group(2);
sectionId = match.group(3) ?? match.group(4);
print('InstanceId: $instanceId');
print('ConfigurationId: $configurationId');
print('SectionId: $sectionId');
} else {
print('L\'URL ne correspond pas au format attendu.');
}
//print("QR CODE FOUND");
/*ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('QR CODE FOUND - ${code.toString()}')),
);*/
VisitAppContext visitAppContext = widget.appContext!.getContext();
if(!visitAppContext.sectionIds!.contains(sectionId) || sectionId == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(TranslationHelper.getFromLocale('invalidQRCode', widget.appContext!.getContext())), backgroundColor: kMainColor2),
);
Navigator.of(context).pop();
} else {
SectionDTO section = visitAppContext.currentSections!.firstWhere((cs) => cs!.id == sectionId)!;
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) {
return SectionPage(configuration: visitAppContext.configuration!, rawSection: null, visitAppContextIn: visitAppContext, sectionId: section.id!);
},
), ),
); );
} }
}
});
});
}
Future<void> _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) async { void _onDetect(BarcodeCapture capture) {
//log('${DateTime.now().toIso8601String()}_onPermissionSet $p'); if (isProcessing) return;
if (!p) {
var status = await Permission.camera.status; final barcode = capture.barcodes.first;
if(!status.isGranted) { final code = barcode.rawValue ?? "";
if (barcode.format == BarcodeFormat.qrCode && code.isNotEmpty) {
isProcessing = true;
RegExp regExp = RegExp(r'^(?:https:\/\/web\.mymuseum\.be\/([^\/]+)\/([^\/]+)\/([^\/]+)|([^\/]+))$'); // myinfomate
var match = regExp.firstMatch(code);
String? instanceId = match?.group(1);
String? configurationId = match?.group(2);
String? sectionId = match?.group(3) ?? match?.group(4);
if (match == null || sectionId == null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('no Permission')), SnackBar(content: Text("L'URL ne correspond pas au format attendu."), backgroundColor: kMainColor2),
); );
Navigator.of(context).pop();
return;
}
// You can request multiple permissions at once. VisitAppContext visitAppContext = widget.appContext!.getContext();
Map<Permission, PermissionStatus> statuses = await [
Permission.camera, if (visitAppContext.sectionIds == null || !visitAppContext.sectionIds!.contains(sectionId)) {
].request(); ScaffoldMessenger.of(context).showSnackBar(
print(statuses[Permission.camera]); SnackBar(content: Text(TranslationHelper.getFromLocale('invalidQRCode', visitAppContext)), backgroundColor: kMainColor2),
print(status); );
Navigator.of(context).pop();
} else {
dynamic rawSection = visitAppContext.currentSections!.firstWhere((cs) => cs!['id'] == sectionId)!;
Navigator.of(context).pop();
Navigator.push(
context,
SlideFromRightRoute(page: SectionPage(
configuration: visitAppContext.configuration!,
rawSection: rawSection,
visitAppContextIn: visitAppContext,
sectionId: rawSection['id'],
)),
);
} }
} }
} }
@override @override
void dispose() { void dispose() {
controller?.dispose(); controller.dispose();
super.dispose(); super.dispose();
} }
} }
showScannerDialog(BuildContext context, AppContext appContext) { showScannerDialog(BuildContext context, AppContext appContext) {
showDialog( showDialog(
context: context,
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => AlertDialog(
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)) borderRadius: BorderRadius.all(Radius.circular(10.0)),
), ),
content: ScannerDialog(appContext: appContext), content: ScannerDialog(appContext: appContext),
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), context: context ),
); );
} }

View File

@ -30,6 +30,7 @@ class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContai
int maxduration = 100; int maxduration = 100;
Duration? durationAudio; Duration? durationAudio;
String currentpostlabel = "00:00"; String currentpostlabel = "00:00";
bool _isDisposed = false;
@override @override
void initState() { void initState() {
@ -107,13 +108,13 @@ class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContai
}); });
});*/ });*/
if(audiobytes != null) { /*if(audiobytes != null) {
print("GOT AUDIOBYYYTES - LOCALLY SOSO"); print("GOT AUDIOBYYYTES - LOCALLY SOSO");
await player.setAudioSource(LoadedSource(audiobytes!)); await player.setAudioSource(LoadedSource(audiobytes!));
} else { } else {
print("GET SOUND BY URL"); print("GET SOUND BY URL");
await player.dynamicSet(url: widget.resourceURl); await player.dynamicSet(url: widget.resourceURl);
} }*/
if(widget.isAuto) { if(widget.isAuto) {
//player.play(BytesSource(audiobytes)); //player.play(BytesSource(audiobytes));
@ -126,10 +127,36 @@ class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContai
} }
}); });
super.initState(); super.initState();
Future.microtask(() async {
try {
if (_isDisposed || !mounted) return;
if (widget.audioBytes != null) {
await player.setAudioSource(LoadedSource(widget.audioBytes!));
} else {
await player.dynamicSet(url: widget.resourceURl);
}
if (_isDisposed || !mounted) return;
if (widget.isAuto) {
await player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}
} catch (e, stack) {
debugPrint('Audio error: $e');
debugPrintStack(stackTrace: stack);
}
});
} }
@override @override
void dispose() { void dispose() {
_isDisposed = true;
player.stop(); player.stop();
player.dispose(); player.dispose();
super.dispose(); super.dispose();
@ -144,10 +171,10 @@ class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContai
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext(); VisitAppContext visitAppContext = appContext.getContext();
Size size = MediaQuery.of(context).size;
return FloatingActionButton( return InkWell(
backgroundColor: Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)).withValues(alpha: 0.7), onTap: () async {
onPressed: () async {
if(!isplaying && !audioplayed){ if(!isplaying && !audioplayed){
//player.play(BytesSource(audiobytes)); //player.play(BytesSource(audiobytes));
//await player.setUrl(widget.resourceURl); //await player.setUrl(widget.resourceURl);
@ -170,6 +197,12 @@ class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContai
}); });
} }
}, },
child: Container(
width: size.width,
decoration: BoxDecoration(
color: Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)).withValues(alpha: 0.7),
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
),
child: isplaying ? Column( child: isplaying ? Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -241,6 +274,7 @@ class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContai
) )
], ],
),*/ ),*/
),
); );
} }
} }

View File

@ -16,7 +16,7 @@ class VisitAppContext with ChangeNotifier {
ConfigurationDTO? configuration; ConfigurationDTO? configuration;
List<String?>? sectionIds; // Use to valid QR code found List<String?>? sectionIds; // Use to valid QR code found
List<BeaconSection?>? beaconSections; List<BeaconSection?>? beaconSections;
List<SectionDTO?>? currentSections; List<dynamic>? currentSections;
List<SectionRead> readSections = []; List<SectionRead> readSections = [];
bool isContentCurrentlyShown = false; bool isContentCurrentlyShown = false;
bool isScanningBeacons = false; bool isScanningBeacons = false;

View File

@ -21,8 +21,6 @@ import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/client.dart';
import 'package:mymuseum_visitapp/constants.dart'; import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../Tests/TestAR.dart';
import 'configurations_list.dart'; import 'configurations_list.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {

View File

@ -25,8 +25,6 @@ import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/client.dart';
import 'package:mymuseum_visitapp/constants.dart'; import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../Tests/TestAR.dart';
import 'configurations_list.dart'; import 'configurations_list.dart';
class HomePage3 extends StatefulWidget { class HomePage3 extends StatefulWidget {

View File

@ -0,0 +1,173 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/tree_node.dart';
class FilterTree extends StatefulWidget {
final List<TreeNode> data;
final bool selectOneToAll;
final Function(TreeNode node, bool checked, int commonId) onChecked;
final Function(TreeNode node, int id) onClicked;
final Color? textColor;
final Color? checkBoxColor;
final EdgeInsets? childrenPadding;
FilterTree(
{this.checkBoxColor,
this.textColor,
this.childrenPadding,
required this.onChecked,
required this.onClicked,
required this.data,
required this.selectOneToAll});
@override
_FilterTree createState() => _FilterTree();
}
class _FilterTree extends State<FilterTree> {
@override
void initState() {
super.initState();
}
Map<String, int> map = {};
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return _buildNode(widget.data[index], EdgeInsets.all(0));
},
itemCount: widget.data.length,
);
}
Widget _buildNode(TreeNode node, EdgeInsets childrenPadding) {
var nonCheckedIcon = Icon(
Icons.check_box_outline_blank,
color: widget.checkBoxColor ?? Colors.green,
);
var checkedIcon = Icon(
Icons.check_box,
color: widget.checkBoxColor ?? Colors.green,
);
// Si le nœud a des enfants, retourne l'ExpansionTile avec les enfants
if (node.children.isNotEmpty) {
return Padding(
padding: childrenPadding,
child: InkWell(
onTap: () {
widget.onClicked(node, node.id);
},
child: ExpansionTile(
iconColor: widget.checkBoxColor,
collapsedIconColor: widget.checkBoxColor,
tilePadding: EdgeInsets.fromLTRB(0, 0, 0, 0),
initiallyExpanded: node.show,
title: Container(
margin: EdgeInsets.only(left: 0),
padding: EdgeInsets.only(left: 0),
child: Text(
node.title,
style: TextStyle(color: widget.textColor ?? Colors.black),
),
),
leading: IconButton(
icon: node.checked ? checkedIcon : nonCheckedIcon,
onPressed: () {
setState(() {
_toggleNodeSelection(node, !node.checked);
widget.onChecked(node, node.checked, node.id);
});
},
),
children: node.children
.map((child) => _buildNode(
child, widget.childrenPadding ?? EdgeInsets.all(0)))
.toList(),
),
),
);
} else {
// Si le nœud n'a pas d'enfants, retourne simplement le titre sans ExpansionTile
// avec le même padding que les éléments avec des enfants
return Padding(
padding: childrenPadding,
child: InkWell(
onTap: () {
widget.onClicked(node, node.id);
},
child: ListTile(
contentPadding: childrenPadding, // Utilisez le padding des enfants
title: Text(
node.title,
style: TextStyle(color: widget.textColor ?? Colors.black),
),
leading: IconButton(
icon: node.checked ? checkedIcon : nonCheckedIcon,
onPressed: () {
setState(() {
_toggleNodeSelection(node, !node.checked);
widget.onChecked(node, node.checked, node.id);
});
},
),
),
),
);
}
}
void _toggleNodeSelection(TreeNode node, bool isChecked) {
node.checked = isChecked;
_updateParentNodes(node, isChecked);
for (var child in node.children) {
_toggleNodeSelection(child, isChecked);
}
}
void _updateParentNodes(TreeNode node, bool checked) {
//First Check it has parent node
bool canContinue = false;
if (node.pid != 0) {
canContinue = true;
}
//if it have parent node then check all childrens are checked or non checked
bool canCheck = true;
if (!checked) {
for (int i = 0; i < node.children.length; i++) {
if (node.children[i].checked != checked) {
canCheck = false;
}
}
}
if (canCheck) {
node.checked = checked;
}
if (canContinue) {
checkEachNode(widget.data, node, checked);
}
// if all childrens are non checked the check parent node in widget.node if it matches node id then check above parent node
}
checkEachNode(List<TreeNode> checkNode, TreeNode node, bool checked) {
bool canStop = false;
for (int i = 0; i < checkNode.length; i++) {
if (checkNode[i].children.isNotEmpty) {
checkEachNode(checkNode[i].children, node, checked);
}
if (checkNode[i].id == node.pid && !canStop) {
canStop = true;
_updateParentNodes(checkNode[i], checked);
break;
}
}
}
}

View File

@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:manager_api_new/api.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/map_context.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:provider/provider.dart';
import 'package:html/parser.dart' show parse;
import 'package:latlong2/latlong.dart' as ll;
class FlutterMapView extends StatefulWidget {
final MapDTO? mapDTO;
final List<GeoPointDTO> geoPoints;
final List<Map<String, dynamic>> icons;
final String? language;
const FlutterMapView({
Key? key,
this.mapDTO,
required this.geoPoints,
required this.icons,
this.language,
}) : super(key: key);
@override
_FlutterMapViewState createState() => _FlutterMapViewState();
}
class _FlutterMapViewState extends State<FlutterMapView> {
late List<GeoPointDTO> markersList;
late List<Marker> markers;
bool filterZoneSelected = false;
MapController? mapController;
List<Marker> createPoints(mapContext) {
markersList = [];
markers = [];
int i = 0;
widget.geoPoints.forEach((point) {
if (point.title!.where((translation) => translation.language == widget.language).isNotEmpty) {
var textSansHTML = parse(point.title!.firstWhere((translation) => translation.language == widget.language).value);
point.id = i;
point.title = point.title!.where((t) => t.language == widget.language).toList();
markersList.add(point);
//var icon = point.categorie == null ? BitmapDescriptor.fromBytes(widget.icons.where((i) => i['id'] == null).first['icon']) : widget.icons.any((i) => i['id'] == point.categorieId) ? BitmapDescriptor.fromBytes(widget.icons.where((i) => i['id'] == point.categorieId).first['icon']) : BitmapDescriptor.fromBytes(widget.icons.where((i) => i['id'] == null).first['icon']); //widget.selectedMarkerIcon,;
markers.add(
Marker(
width: 80.0,
height: 80.0,
point: LatLng(double.tryParse(point.latitude!)!, double.tryParse(point.longitude!)!),
child: GestureDetector(
onTap: () {
/*final mapContext = Provider.of<MapContext>(context, listen: false);
mapContext.setSelectedPoint(point);
mapContext.setSelectedPointForNavigate(point);*/
mapContext.setSelectedPoint(point);
//mapContext.setSelectedPointForNavigate(point);
},
child: widget.icons.firstWhere((i) => i['id'] == point.categorieId, orElse: () => widget.icons.first)['icon'] != null ? Image.memory(widget.icons.firstWhere((i) => i['id'] == point.categorieId, orElse: () => widget.icons.first)['icon']) : Icon(Icons.pin_drop, color: Colors.red),//widget.icons.firstWhere((i) => i['id'] == point.categorieId, orElse: () => widget.icons.first)['icon'],
)
),
);
i++;
}
});
return markers;
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final mapContext = Provider.of<MapContext>(context);
final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
markers = createPoints(mapContext);
if (mapController == null) { // mapContext.getSelectedPointForNavigate() != null
/*var geoPoint = mapContext.getSelectedPointForNavigate();
var center = LatLng(double.tryParse(geoPoint.latitude!)!, double.tryParse(geoPoint.longitude!)!);*/
mapController = MapController();
//mapController!.move(center, widget.mapDTO!.zoom != null ? widget.mapDTO!.zoom!.toDouble() : 12);
mapContext.setSelectedPointForNavigate(null); // Reset after navigation
}
return Stack(
children: [
FlutterMap(
mapController: mapController,
options: MapOptions(
initialCenter: widget.mapDTO!.longitude != null && widget.mapDTO!.latitude != null ? ll.LatLng(double.tryParse(widget.mapDTO!.latitude!)!, double.tryParse(widget.mapDTO!.longitude!)!) : ll.LatLng(4.865105, 50.465503), //.toJson()
initialZoom: widget.mapDTO!.zoom != null ? widget.mapDTO!.zoom!.toDouble() : 12,
onTap: (Tap, lnt) => {
mapContext.setSelectedPointForNavigate(null),
mapContext.setSelectedPoint(null),
}
),
children: [
TileLayer(
urlTemplate: "https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}",
userAgentPackageName: 'be.unov.myinfomate.tablet',
),
MarkerLayer(
markers: markers
),
],
),
Container(
child: Builder(
builder: (context) {
return Consumer<MapContext>(
builder: (context, mapContext, _) {
var geoPoint = mapContext.getSelectedPointForNavigate() as GeoPointDTO?;
if (geoPoint != null && mapController != null) {
print("COUCOU IL FAUT NAVIGATE FLUTTER MAP");
mapController!.move(LatLng(double.tryParse(geoPoint.latitude!)!, double.tryParse(geoPoint.longitude!)!), mapController!.camera.zoom/*, widget.mapDTO!.zoom != null ? widget.mapDTO!.zoom!.toDouble() : 16*/); // keep actual zoom
}
return SizedBox();
},
);
}
),
)
],
);
}
}

View File

@ -0,0 +1,390 @@
import 'dart:ui';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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/Sections/Map/filter_tree.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/tree_node.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart';
import 'package:html/parser.dart' show parse;
import 'package:diacritic/diacritic.dart';
import 'map_context.dart';
class GeoPointFilter extends StatefulWidget {
final String language;
final List<GeoPointDTO> geoPoints;
final List<CategorieDTO> categories;
final Function(List<GeoPointDTO>?) filteredPoints;
final MapProvider provider;
GeoPointFilter({required this.language, required this.geoPoints, required this.categories, required this.filteredPoints, required this.provider});
@override
_GeoPointFilterState createState() => _GeoPointFilterState();
}
class _GeoPointFilterState extends State<GeoPointFilter> with SingleTickerProviderStateMixin {
List<GeoPointDTO> selectedGeoPoints = [];
late List<TreeNode> _filteredNodes;
late ValueNotifier<String> _searchTextNotifier;
final TextEditingController _searchController = TextEditingController();
FocusNode focusNode = FocusNode();
bool _isExpanded = false;
bool _showContent = false;
late AnimationController _controller;
late Animation<double> _widthAnimation;
late Size screenSize;
@override
void didChangeDependencies() {
super.didChangeDependencies();
screenSize = MediaQuery.of(context).size;
}
@override
void initState() {
super.initState();
_searchTextNotifier = ValueNotifier<String>('');
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
_widthAnimation = Tween<double>(begin: 40, end: 350).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
_filteredNodes = buildTreeNodes(widget.categories, widget.geoPoints);
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
void _toggleExpansion() {
setState(() {
if (_isExpanded) {
_showContent = false;
_isExpanded = false;
} else {
_isExpanded = true;
Future.delayed(const Duration(milliseconds: 300), () {
if (_isExpanded) {
setState(() => _showContent = true);
}
});
}
_isExpanded ? _controller.forward() : _controller.reverse();
});
}
List<TreeNode> buildTreeNodes(List<CategorieDTO> categories, List<GeoPointDTO> geoPoints) {
List<TreeNode> nodes = [];
// Pour chaque point sans categorie, créer un noeud
for(var pointWithoutCat in geoPoints.where((gp) => gp.categorieId == null))
{
if(pointWithoutCat.title!.where((l) => l.language == widget.language).firstOrNull != null) {
TreeNode nodeWithoutCat = TreeNode(
id: 000 + int.parse(
(pointWithoutCat.latitude ?? '').substring(0, min(pointWithoutCat.latitude!.length, 10)).replaceAll(".", "").replaceAll("-","") + (pointWithoutCat.longitude ?? '').substring(0, min(pointWithoutCat.longitude!.length, 10)).replaceAll(".", "").replaceAll("-","")
),
title: parse(pointWithoutCat.title!.firstWhere((l) => l.language == widget.language).value!).documentElement!.text,
children: [],
checked: true, // default true
show: false,
pid: 0,
commonID: 0,
);
nodes.add(nodeWithoutCat);
}
}
// Pour chaque catégorie, créez un nœud parent
for (var category in categories) {
if(category.label!.where((l) => l.language == widget.language).firstOrNull != null)
{
TreeNode categoryNode = TreeNode(
id: 100 + (category.id ?? 0),
title: parse(category.label!.firstWhere((l) => l.language == widget.language).value!).documentElement!.text,
children: [],
checked: true, // default true
show: false,
pid: 0,
commonID: 0,
);
// Ajoutez les géopoints correspondant à cette catégorie en tant qu'enfants du nœud parent
for (var geoPoint in geoPoints.where((gp) => gp.categorieId != null)) {
if (geoPoint.categorieId == category.id && geoPoint.title!.where((l) => l.language == widget.language).firstOrNull != null) {
TreeNode geoPointNode = TreeNode(
id: 000 + int.parse(
(geoPoint.latitude ?? '').substring(0, min(geoPoint.latitude!.length, 10)).replaceAll(".", "").replaceAll("-", "") + (geoPoint.longitude ?? '').substring(0, min(geoPoint.longitude!.length, 10)).replaceAll(".", "").replaceAll("-", "")
),
title: parse(geoPoint.title!.firstWhere((l) => l.language == widget.language).value!).documentElement!.text,
checked: true, // default true
show: false,
children: [],
pid: 0,
commonID: 0,
);
categoryNode.children.add(geoPointNode);
}
}
nodes.add(categoryNode);
}
}
nodes.sort((a, b) => a.title.compareTo(b.title));
return nodes;
}
void filterNodes() {
String searchText = _searchController.text;
setState(() {
_filteredNodes = searchText.isEmpty
? buildTreeNodes(widget.categories, widget.geoPoints)
: _filterNodesBySearchText(searchText);
// if unfocus, then
//if(searchText.isEmpty) {
// widget.filteredPoints = //todo
sendFilteredGeoPoint();
//}
});
}
sendFilteredGeoPoint() {
List<GeoPointDTO> checkedGeoPoints = [];
// Parcourez les nœuds filtrés pour récupérer les GeoPointDTO correspondants qui sont cochés
for (var node in _filteredNodes) {
if (node.children.isNotEmpty) {
for (var childNode in node.children) {
if (childNode.checked) {
var point = widget.geoPoints.firstWhere(
(point) {
String latitudePart = (point.latitude ?? '')
.substring(0, min(point.latitude!.length, 10))
.replaceAll(".", "")
.replaceAll("-", "");
String longitudePart = (point.longitude ?? '')
.substring(0, min(point.longitude!.length, 10))
.replaceAll(".", "")
.replaceAll("-", "");
int combinedValue = int.parse(latitudePart + longitudePart);
return combinedValue == childNode.id;
},
orElse: () => GeoPointDTO(id: -1),
);
if (point.id != -1) {
checkedGeoPoints.add(point);
}
}
}
} else {
if(node.checked) {
var point = widget.geoPoints.firstWhere(
(point) {
String latitudePart = (point.latitude ?? '')
.substring(0, min(point.latitude!.length, 10))
.replaceAll(".", "")
.replaceAll("-", "");
String longitudePart = (point.longitude ?? '')
.substring(0, min(point.longitude!.length, 10))
.replaceAll(".", "")
.replaceAll("-", "");
int combinedValue = int.parse(latitudePart + longitudePart);
return combinedValue == node.id;
},
orElse: () => GeoPointDTO(id: -1),
);
if (point.id != -1) {
checkedGeoPoints.add(point);
}
}
}
}
// Passez la liste des GeoPointDTO cochés à la fonction filteredPoints
widget.filteredPoints(checkedGeoPoints);
}
List<TreeNode> _filterNodesBySearchText(String searchText) {
List<TreeNode> filteredNodes = [];
for (var node in buildTreeNodes(widget.categories, widget.geoPoints)) {
if (_nodeOrChildrenContainsText(node, searchText)) {
if(node.children.isNotEmpty) {
for (var childNode in node.children) {
if (_nodeOrChildrenContainsText(childNode, searchText)) {
filteredNodes.add(childNode);
}
}
} else {
filteredNodes.add(node);
}
}
}
return filteredNodes;
}
bool _nodeOrChildrenContainsText(TreeNode node, String searchText) {
// Remove accent and other special characters
String normalizedSearchText = removeDiacritics(searchText.toLowerCase());
String normalizedTitle = removeDiacritics(node.title.toLowerCase());
if (normalizedTitle.contains(normalizedSearchText)) {
return true;
}
for (var childNode in node.children) {
if (_nodeOrChildrenContainsText(childNode, searchText)) {
return true;
}
}
return false;
}
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext();
var currentLanguage = visitAppContext.language;
final mapContext = Provider.of<MapContext>(context);
var primaryColor = visitAppContext.configuration != null ? visitAppContext.configuration!.primaryColor != null ? Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : kSecondColor : kSecondColor;
double rounded = visitAppContext.configuration?.roundedValue?.toDouble() ?? 20.0;
return Positioned(
left: 0,
top: _isExpanded ? screenSize.height * 0.11 : screenSize.height / 2 - (75 / 2),
child: AnimatedBuilder(
animation: _widthAnimation,
builder: (context, child) {
return Container(
width: _widthAnimation.value,
height: _isExpanded ? screenSize.height*0.78 : 75,
decoration: BoxDecoration(
color: _isExpanded ? kBackgroundColor.withValues(alpha: 0.9) : primaryColor.withValues(alpha: 0.8),
borderRadius: BorderRadius.only(topRight: Radius.circular(rounded), bottomRight: Radius.circular(rounded)),
),
child: _showContent ? Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
IconButton(
icon: Icon(Icons.close, color: primaryColor),
onPressed: _toggleExpansion,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: 60,
width: (screenSize.width * 0.76),
child: Padding(
padding: const EdgeInsets.only(left: 10.0, bottom: 8.0, top: 4.0),
child: TextField(
focusNode: focusNode,
controller: _searchController,
onChanged: (value) {
filterNodes();
},
cursorColor: Colors.black,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
labelText: _searchController.text.isEmpty ? TranslationHelper.getFromLocale("map.search", appContext.getContext()) : "",
labelStyle: const TextStyle(
color: Colors.black
),
focusColor: primaryColor,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0)),
borderSide: BorderSide(color: primaryColor)),
//labelStyle: TextStyle(color: primaryColor),
suffixIcon: IconButton(
icon: _searchController.text.isNotEmpty ? Icon(Icons.close, color: primaryColor) : Icon(Icons.search, color: primaryColor),
onPressed: () {
if(_searchController.text.isNotEmpty) {
_searchController.text = "";
// TODO reset view ?
}
filterNodes();
if(_searchController.text.isNotEmpty) {
focusNode.unfocus();
}
},
),
),
),
),
),
],
),
ValueListenableBuilder<String>(
valueListenable: _searchTextNotifier,
builder: (context, value, _) {
return SizedBox(
width: screenSize.width * 0.8,
height: screenSize.height * 0.63,
child: FilterTree(
data: _filteredNodes,
selectOneToAll: true,
textColor: Colors.black,
onChecked: (node, checked, commonID) {
sendFilteredGeoPoint();
},
onClicked: (node, commonID) {
var selectedNode = widget.geoPoints.firstWhere(
(point) {
String latitudePart = (point.latitude ?? '')
.substring(0, min(point.latitude!.length, 10))
.replaceAll(".", "")
.replaceAll("-", "");
String longitudePart = (point.longitude ?? '')
.substring(0, min(point.longitude!.length, 10))
.replaceAll(".", "")
.replaceAll("-", "");
int combinedValue =
int.parse(latitudePart + longitudePart);
return combinedValue == commonID;
},
orElse: () => GeoPointDTO(id: -1),
);
if (selectedNode.id != -1) {
mapContext.setSelectedPointForNavigate(selectedNode);
_toggleExpansion();
} else {
print('Aucun point correspondant trouvé.');
}
},
checkBoxColor: primaryColor,
childrenPadding: const EdgeInsets.only(
left: 20, top: 10, right: 0, bottom: 10),
),
);
}
),
],
),
) : IconButton(
icon: const Icon(Icons.search, color: Colors.white),
onPressed: _toggleExpansion,
),
);
}
),
);
}
}

View File

@ -0,0 +1,239 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:manager_api_new/api.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/map_context.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:provider/provider.dart';
import 'package:html/parser.dart' show parse;
class GoogleMapView extends StatefulWidget {
final MapDTO mapDTO;
final List<GeoPointDTO> geoPoints;
final List<Map<String, dynamic>> icons;
final String? language;
const GoogleMapView({
Key? key,
required this.mapDTO,
required this.geoPoints,
required this.icons,
this.language,
}) : super(key: key);
@override
_GoogleMapViewState createState() => _GoogleMapViewState();
}
class _GoogleMapViewState extends State<GoogleMapView> {
ConfigurationDTO? configurationDTO;
Completer<GoogleMapController> _controller = Completer();
GoogleMapController? _GoogleMapcontroller;
Set<Marker> markers = {};
List<GeoPointDTO>? pointsToShow = [];
//List<String>? selectedCategories = [];
bool init = false;
Set<Marker> getMarkers(language, mapContext) {
markers = {};
int i = 0;
pointsToShow!.forEach((point) {
var textSansHTML = parse(point.title!.firstWhere((translation) => translation.language == language).value);
point.id = i;
/*var mapMarker = new MapMarker(
id: point.id,
title: parse(textSansHTML.body!.text).documentElement!.text,
description: point.description!.firstWhere((translation) => translation.language == language).value,
longitude: point.longitude,
latitude: point.latitude,
contents: point.contents
);*/
if (point.latitude != null && point.longitude != null) {
var icon = point.categorieId == null ? BitmapDescriptor.bytes(widget.icons.where((i) => i['id'] == null).first['icon']) : widget.icons.any((i) => i['id'] == point.categorieId) ? BitmapDescriptor.bytes(widget.icons.where((i) => i['id'] == point.categorieId).first['icon']) : BitmapDescriptor.bytes(widget.icons.where((i) => i['id'] == null).first['icon']); //widget.selectedMarkerIcon,;
markers.add(Marker(
draggable: false,
markerId: MarkerId(parse(textSansHTML.body!.text).documentElement!.text + point.latitude! + point.longitude!),
position: LatLng(
double.tryParse(point.latitude!)!,
double.tryParse(point.longitude!)!,
),
icon: icon, //widget.selectedMarkerIcon,
//widget.selectedMarkerIcon != null ? BitmapDescriptor.fromBytes(widget.selectedMarkerIcon!) : BitmapDescriptor.defaultMarker,
/*icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueYellow,
),*/
onTap: () {
//setState(() {
mapContext.setSelectedPoint(point);
//mapContext.setSelectedPointForNavigate(point);
//});
},
infoWindow: InfoWindow.noText));
}
i++;
});
return markers;
}
@override
void initState() {
//selectedCategories = widget.mapDTO.categories!.map((cat) => cat.label!.firstWhere((element) => element.language == widget.language).value!).toList();
super.initState();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
@override
Widget build(BuildContext context) {
final mapContext = Provider.of<MapContext>(context);
final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
Size size = MediaQuery.of(context).size;
pointsToShow = widget.geoPoints;
/*if(!init) {
print("getmarkers in build");*/
getMarkers(widget.language, mapContext);
/* init = true;
}*/
//MapTypeApp? mapTypeApp = MapTypeApp.fromJson(widget.mapDTO!.mapType!.value);
//print(mapTypeApp.toString());
MapType type = MapType.hybrid;
//if(kIsWeb) {
if(widget.mapDTO.mapType != null) {
switch(widget.mapDTO.mapType!.value) {
case 0:
type = MapType.none;
break;
case 1:
type = MapType.normal;
break;
case 2:
type = MapType.satellite;
break;
case 3:
type = MapType.terrain;
break;
case 4:
type = MapType.hybrid;
break;
}
}
/*} else {
print("is OTHEER");
type = EnumToString.fromString(MapType.values, widget.mapDTO!.mapType.toString()) != null ? EnumToString.fromString(MapType.values, widget.mapDTO!.mapType.toString())! : MapType.hybrid;
}*/
//MapType type = EnumToString.fromString(MapType.values, widget.mapDTO!.mapType.toString()) != null ? EnumToString.fromString(MapType.values, widget.mapDTO!.mapType.toString())! : MapType.hybrid;
return Stack(
children: [
Center(
child: GoogleMap(
mapType: type, // widget.mapDTO!.mapType != null ? EnumToString.fromString(MapType.values, widget.mapDTO!.mapType.toString())!: MapType.hybrid,
mapToolbarEnabled: false,
indoorViewEnabled: false,
initialCameraPosition: CameraPosition(
target: widget.mapDTO.longitude != null && widget.mapDTO.latitude != null ? LatLng(double.tryParse(widget.mapDTO.latitude!)!, double.tryParse(widget.mapDTO.longitude!)!) : LatLng(50.465503, 4.865105) , // MDLF 50.416639, 4.879169 / Namur 50.465503, 4.865105
zoom: widget.mapDTO.zoom != null ? widget.mapDTO.zoom!.toDouble() : 18,
),
onMapCreated: (GoogleMapController controller) {
if(kIsWeb) {
//_controllerWeb.complete(controller);
} else {
_controller.complete(controller);
_GoogleMapcontroller = controller;
}
},
markers: markers,
onTap: (LatLng location) {
print(location);
mapContext.setSelectedPoint(null);
mapContext.setSelectedPointForNavigate(null);
},
),
),
Container(
child: Builder(
builder: (context) {
return Consumer<MapContext>(
builder: (context, mapContext, _) {
var geopoint = mapContext.getSelectedPointForNavigate() as GeoPointDTO?;
if (geopoint != null && _GoogleMapcontroller != null) {
print("COUCOU IL FAUT NAVUGATE");
// TODO Handle zoomDetail
_GoogleMapcontroller!.getZoomLevel().then((actualZoom) {
var zoomToNavigate = actualZoom <= 12.0 ? 15.0 : actualZoom;
_GoogleMapcontroller!.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(double.tryParse(geopoint.latitude!)!, double.tryParse(geopoint.longitude!)!),
tilt: 0.0,
bearing: 0.0,
zoom: zoomToNavigate
//zoom: widget.mapDTO.zoom != null ? widget.mapDTO.zoom!.toDouble() : 16
)
));
});
}
return SizedBox();
},
);
}
),
)
/*Positioned(
left: 5,
top: 35,
child: SizedBox(
width: size.width * 0.3,
height: size.height * 0.76,
child: GeoPointFilter(
language: tabletAppContext.language!,
geoPoints: widget.mapDTO!.points!,
categories: widget.mapDTO!.categories!,
filteredPoints: (filteredPoints) {
print("COUCOU FILTERED POINTS");
print(filteredPoints);
}),
),
),
Positioned(
bottom: 35,
left: 10,
child: SizedBox(
width: size.width * 0.75,
child: MultiSelectContainer(
label: null,
color: kBackgroundGrey,
initialValue: selectedCategories!,
isMultiple: true,
values: widget.mapDTO!.categories!.map((categorie) => categorie.label!.firstWhere((element) => element.language == widget.language).value!).toList(),
onChanged: (value) {
var tempOutput = new List<String>.from(value);
print(tempOutput);
if(init) {
selectedCategories = tempOutput;
pointsToShow = widget.mapDTO!.points!.where((point) => tempOutput.any((tps) => point.categorie?.label?.firstWhere((lab) => lab.language == widget.language).value == tps) || point.categorie == null).toList();
setState(() {
markers = getMarkers(widget.language, mapContext);
mapContext.notifyListeners();
});
}
},
),
),
),*/
],
);
}
}

View File

@ -0,0 +1,236 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:manager_api_new/api.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mapBox;
import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/map_context.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/map_page.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:provider/provider.dart';
import 'package:html/parser.dart' show parse;
class MapBoxView extends StatefulWidget {
final MapDTO? mapDTO;
final List<GeoPointDTO> geoPoints;
final List<Map<String, dynamic>> icons;
final String? language;
const MapBoxView({
Key? key,
this.mapDTO,
required this.geoPoints,
required this.icons,
this.language,
}) : super(key: key);
@override
_MapBoxViewState createState() => _MapBoxViewState();
}
class AnnotationClickListener extends mapBox.OnPointAnnotationClickListener {
late List<GeoPointDTO> markersList;
late MapContext mapContext;
AnnotationClickListener({
required this.markersList,
required this.mapContext,
});
@override
void onPointAnnotationClick(mapBox.PointAnnotation annotation) {
try{
var markerToShow = markersList.firstWhere((ml) => "${parse(ml.title!.first.value).documentElement!.text}${ml.latitude}${ml.longitude}" == annotation.textField);
mapContext.setSelectedPoint(markerToShow);
//mapContext.setSelectedPointForNavigate(markerToShow);
} catch(e) {
print("ISSSUE setSelectedMarker");
print(e);
}
}
}
class _MapBoxViewState extends State<MapBoxView> {
late mapBox.MapboxMap? mapboxMap;
mapBox.PointAnnotationManager? pointAnnotationManager;
bool filterZoneSelected = false;
createPoints() {
var options = <mapBox.PointAnnotationOptions>[];
int i = 0;
markersList = [];
pointsToShow!.forEach((point) {
if(point.title!.where((translation) => translation.language == widget.language).firstOrNull != null) {
var textSansHTML = parse(point.title!.firstWhere((translation) => translation.language == widget.language).value);
point.id = i;
point.title = point.title!.where((t) => t.language == widget.language).toList();
/*var mapMarker = new MapMarker(
id: i,
title: parse(textSansHTML.body!.text).documentElement!.text,
description: point.description!.firstWhere((translation) => translation.language == widget.language).value,
longitude: point.longitude,
latitude: point.latitude,
contents: point.contents
);*/
markersList.add(point);
options.add(mapBox.PointAnnotationOptions(
geometry: mapBox.Point(
coordinates: mapBox.Position(
double.tryParse(point.longitude!)!,
double.tryParse(point.latitude!)!,
)), // .toJson()
iconSize: 1.3,
textField: "${parse(textSansHTML.body!.text).documentElement!.text}${point.latitude}${point.longitude}",
textOpacity: 0.0,
iconOffset: [0.0, 0.0],
symbolSortKey: 10,
iconColor: 0,
iconImage: null,
image: point.categorieId == null ? widget.icons.where((i) => i['id'] == null).first['icon'] : widget.icons.any((i) => i['id'] == point.categorieId) ? widget.icons.where((i) => i['id'] == point.categorieId).first['icon'] : widget.icons.where((i) => i['id'] == null).first['icon'], //widget.selectedMarkerIcon,
)); // ,
i++;
}
});
print(options.length);
return options;
}
_onMapCreated(mapBox.MapboxMap mapboxMap, MapContext mapContext) {
this.mapboxMap = mapboxMap;
mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async {
this.pointAnnotationManager = pointAnnotationManager;
pointAnnotationManager.createMulti(createPoints());
pointAnnotationManager.addOnPointAnnotationClickListener(AnnotationClickListener(mapContext: mapContext, markersList: markersList));
init = true;
});
}
ConfigurationDTO? configurationDTO;
//Completer<GoogleMapController> _controller = Completer();
//Set<Marker> markers = {};
List<GeoPointDTO>? pointsToShow = [];
List<String>? selectedCategories = [];
bool init = false;
@override
void initState() {
pointsToShow = widget.geoPoints;//widget.mapDTO!.points;
var nonNullCat = widget.mapDTO!.categories!.where((c) => c.label!.where((element) => element.language == widget.language).firstOrNull != null);
selectedCategories = nonNullCat.map((categorie) => categorie.label!.firstWhere((element) => element.language == widget.language).value!).toList();
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final mapContext = Provider.of<MapContext>(context);
pointsToShow = widget.geoPoints;//widget.mapDTO!.points;
if(pointAnnotationManager != null) {
pointAnnotationManager!.deleteAll();
pointAnnotationManager!.createMulti(createPoints());
//mapContext.notifyListeners();
}
final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
Size size = MediaQuery.of(context).size;
var type = mapBox.MapboxStyles.STANDARD;
if(widget.mapDTO!.mapTypeMapbox != null) {
switch(widget.mapDTO!.mapTypeMapbox!) {
case MapTypeMapBox.standard:
type = mapBox.MapboxStyles.STANDARD;
break;
case MapTypeMapBox.streets:
type = mapBox.MapboxStyles.MAPBOX_STREETS;
break;
case MapTypeMapBox.outdoors:
type = mapBox.MapboxStyles.OUTDOORS;
break;
case MapTypeMapBox.light:
type = mapBox.MapboxStyles.LIGHT;
break;
case MapTypeMapBox.dark:
type = mapBox.MapboxStyles.DARK;
break;
case MapTypeMapBox.satellite:
type = mapBox.MapboxStyles.SATELLITE;
break;
case MapTypeMapBox.satellite_streets:
type = mapBox.MapboxStyles.SATELLITE_STREETS;
break;
}
}
return Stack(
children: [
Center(
child: mapBox.MapWidget(
key: ValueKey("mapBoxWidget"),
styleUri: type,
onMapCreated: (maBoxMap) {
_onMapCreated(maBoxMap, mapContext);
},
onTapListener: (listener) {
// close on tap
mapContext.setSelectedPoint(null);
mapContext.setSelectedPointForNavigate(null);
},
cameraOptions: mapBox.CameraOptions(
center: mapBox.Point(coordinates: widget.mapDTO!.longitude != null && widget.mapDTO!.latitude != null ? mapBox.Position(double.tryParse(widget.mapDTO!.longitude!)!, double.tryParse(widget.mapDTO!.latitude!)!) : mapBox.Position(4.865105, 50.465503)), //.toJson()
zoom: widget.mapDTO!.zoom != null ? widget.mapDTO!.zoom!.toDouble() : 12),
)
),
Container(
child: Builder(
builder: (context) {
return Consumer<MapContext>(
builder: (context, mapContext, _) {
var geoPoint = mapContext.getSelectedPointForNavigate() as GeoPointDTO?;
if (geoPoint != null && mapboxMap != null) {
print("COUCOU IL FAUT NAVUGATE MAPBOX");
// TODO Handle zoomDetail
mapboxMap?.easeTo(
mapBox.CameraOptions(
center: mapBox.Point(coordinates: mapBox.Position(double.tryParse(geoPoint.longitude!)!, double.tryParse(geoPoint.latitude!)!)), //.toJson()
zoom: 16,
bearing: 0,
pitch: 3),
mapBox.MapAnimationOptions(duration: 2000, startDelay: 0));
}
return SizedBox();
},
);
}
),
)
/*Positioned(
left: 5,
top: 35,
child: SizedBox(
width: size.width * 0.3,
height: size.height * 0.76,
child: GeoPointFilter(
language: tabletAppContext.language!,
geoPoints: widget.mapDTO!.points!,
categories: widget.mapDTO!.categories!,
filteredPoints: (filteredPoints) {
print("COUCOU FILTERED POINTS");
print(filteredPoints);
}),
),
),*/
],
);
}
}

View File

@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
class MapContext with ChangeNotifier {
GeoPointDTO? _selectedPoint;
GeoPointDTO? _selectedPointNavigate;
MapContext(this._selectedPoint);
getSelectedPoint() => _selectedPoint;
setSelectedPoint(GeoPointDTO? selectedPoint) {
_selectedPoint = selectedPoint;
notifyListeners();
}
getSelectedPointForNavigate() => _selectedPointNavigate;
setSelectedPointForNavigate(GeoPointDTO? selectedPointNavigate) {
_selectedPointNavigate = selectedPointNavigate;
notifyListeners();
}
}

View File

@ -0,0 +1,188 @@
//import 'dart:async';
import 'dart:convert';
// 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// 'package:flutter/services.dart';
//import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:manager_api_new/api.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/flutter_map_view.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/geo_point_filter.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/map_box_view.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/marker_view.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart';
/*import 'package:tablet_app/Components/loading.dart';
import 'package:tablet_app/Components/loading_common.dart';*/
//import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
//import 'package:http/http.dart' as http;
import 'google_map_view.dart';
//import 'package:image/image.dart' as IMG;
//Set<Marker> markers = {};
List<GeoPointDTO> markersList = [];
class MapPage extends StatefulWidget {
final MapDTO section;
final List<Map<String, dynamic>> icons;
MapPage({Key? key, required this.section, required this.icons}) : super(key: key);
@override
_MapPage createState() => _MapPage();
}
class _MapPage extends State<MapPage> {
MapDTO? mapDTO;
//Completer<GoogleMapController> _controller = Completer();
//Uint8List? selectedMarkerIcon;
late ValueNotifier<List<GeoPointDTO>> _geoPoints = ValueNotifier<List<GeoPointDTO>>([]);
/*Future<Uint8List> 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();
}*/
@override
void initState() {
//mapDTO = MapDTO.fromJson(jsonDecode(widget.section.data!));
mapDTO = widget.section;
_geoPoints.value = mapDTO!.points!;
super.initState();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
/*static final CameraPosition _kLake = CameraPosition(
bearing: 192.8334901395799,
target: LatLng(37.43296265331129, -122.08832357078792),
tilt: 59.440717697143555,
zoom: 59.151926040649414);*/
@override
Widget build(BuildContext context) {
//final mapContext = Provider.of<MapContext>(context);
final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
var primaryColor = visitAppContext.configuration != null ? visitAppContext.configuration!.primaryColor != null ? Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : kSecondColor : kSecondColor;
/*return FutureBuilder(
future: getByteIcon(mapDTO!.iconSource),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
child: LoadingCommon()
)
);
}
}
);*/
return MediaQuery.removeViewInsets(
context: context,
removeBottom: true,
child: Stack(
fit: StackFit.passthrough,
children: <Widget>[
ValueListenableBuilder<List<GeoPointDTO>>(
valueListenable: _geoPoints,
builder: (context, value, _) {
switch(mapDTO!.mapProvider) {
case MapProvider.Google:
return GoogleMapView(language: appContext.getContext().language, geoPoints: value, mapDTO: mapDTO!, icons: widget.icons);
case MapProvider.MapBox:
return MapBoxView(language: appContext.getContext().language, geoPoints: value, mapDTO: mapDTO, icons: widget.icons);
// If mapbox bug as 3.24 flutter, we can test via this new widget
return FlutterMapView(language: appContext.getContext().language, geoPoints: value, mapDTO: mapDTO, icons: widget.icons);
default:
// By default google
return GoogleMapView(language: appContext.getContext().language, geoPoints: value, mapDTO: mapDTO!, icons: widget.icons);
}
}
),
GeoPointFilter(
language: visitAppContext.language!,
geoPoints: mapDTO!.points!,
categories: mapDTO!.categories!,
provider: mapDTO!.mapProvider == null ? MapProvider.Google : mapDTO!.mapProvider!,
filteredPoints: (value) {
_geoPoints.value = value!;
}),
MarkerViewWidget(),
Positioned(
top: 35,
left: 10,
child: SizedBox(
width: 50,
height: 50,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
decoration: BoxDecoration(
color: primaryColor,
shape: BoxShape.circle,
),
child: const Icon(Icons.arrow_back, size: 23, color: Colors.white)
),
)
),
),
]
/*floatingActionButton: FloatingActionButton.extended(
onPressed: _goToTheLake,
label: Text('To the lake!'),
icon: Icon(Icons.directions_boat),
),*/
),
);
}
/*getByteIcon(String? source) async {
if(source != null) {
if(kIsWeb) {
Uint8List fileData = await http.readBytes(Uri.parse(source));
selectedMarkerIcon = resizeImage(fileData, 40);
} else {
final ByteData imageData = await NetworkAssetBundle(Uri.parse(source)).load("");
selectedMarkerIcon = await getBytesFromAsset(imageData, 50);
}
} else {
// default icon
final ByteData bytes = await rootBundle.load('assets/icons/marker.png');
selectedMarkerIcon = await getBytesFromAsset(bytes, 25);
}
}*/
/*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;
}*/
/*Future<void> _goToTheLake() async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
}*/
}

View File

@ -0,0 +1,692 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/cupertino.dart';
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/Components/show_element_for_resource.dart';
import 'package:mymuseum_visitapp/Helpers/ImageCustomProvider.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/app_context.dart';
import 'package:photo_view/photo_view.dart';
import 'package:provider/provider.dart';
import 'package:html/parser.dart' show parse;
import 'package:qr_flutter/qr_flutter.dart';
import '../../../constants.dart';
import 'map_context.dart';
class MarkerViewWidget extends StatefulWidget {
MarkerViewWidget();
@override
_MarkerInfoWidget createState() => _MarkerInfoWidget();
}
class _MarkerInfoWidget extends State<MarkerViewWidget> {
CarouselSliderController? sliderController;
ValueNotifier<int> currentIndex = ValueNotifier<int>(1);
@override
void initState() {
sliderController = CarouselSliderController();
super.initState();
}
@override
void dispose() {
sliderController = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
int breakPointPrice = 50;
Size size = MediaQuery.of(context).size;
final mapContext = Provider.of<MapContext>(context);
final appContext = Provider.of<AppContext>(context);
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
var language = visitAppContext.language;
GeoPointDTO? selectedPoint = mapContext.getSelectedPoint() as GeoPointDTO?;
ScrollController scrollDescription = new ScrollController();
ScrollController scrollPrice = new ScrollController();
var isPointPrice = selectedPoint != null && selectedPoint.prices != null && selectedPoint.prices!.isNotEmpty && selectedPoint.prices!.any((d) => d.language == language) && selectedPoint.prices!.firstWhere((d) => d.language == language).value != null && selectedPoint.prices!.firstWhere((d) => d.language == language).value!.trim().length > 0;
Color primaryColor = new Color(int.parse(visitAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16));
Size sizeMarker = Size(size.width * 0.93, size.height * 0.83);
return Center(
child: Visibility(
visible: selectedPoint != null,
child: Container(
width: sizeMarker.width,
height: sizeMarker.height,
margin: EdgeInsets.symmetric(vertical: 3, horizontal: 4),
decoration: BoxDecoration(
color: kBackgroundColor, // Colors.amberAccent //kBackgroundLight,
//shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 10.0),
/*boxShadow: [
BoxShadow(
color: kBackgroundSecondGrey,
spreadRadius: 0.5,
blurRadius: 1.1,
offset: Offset(0, 1.1), // changes position of shadow
),
],*/
),
child: Stack(
children: <Widget> [
Positioned(
right: 5,
top: 5,
child: InkWell(
onTap: () {
setState(() {
mapContext.setSelectedPoint(null);
mapContext.setSelectedPointForNavigate(null);
});
},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: kBackgroundGrey,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: kBackgroundGrey,
spreadRadius: 0.5,
blurRadius: 1.1,
offset: Offset(0, 1.1), // changes position of shadow
),
],
),
child: Icon(
Icons.close,
size: 25,
color: Colors.white,
),
),
),
),
if(selectedPoint != null)
Row(
children: [
selectedPoint.imageResourceId != null && selectedPoint.imageUrl != null ? ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0), bottomLeft: Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0)),
child: Container(
child: Center(
child: Container(
decoration: BoxDecoration(
border: Border(right: BorderSide(width: 0.05, color: kMainGrey)),
color: Colors.grey,
),
width: size.width * 0.17,
height: size.height,
child: CachedNetworkImage(
imageUrl: selectedPoint.imageUrl!,
width: size.width,
fit: BoxFit.cover,
progressIndicatorBuilder: (context, url, downloadProgress) {
return Center(
child: SizedBox(
width: 50,
height: 50,
child: LoadingCommon(),
),
);
},
errorWidget: (context, url, error) => Icon(Icons.error),
),
),
)
),
): SizedBox(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 8.0, left: 15.0),
child: Container(
//color: Colors.green,
height: size.height * 0.06,
width: size.width * 0.65,
child: HtmlWidget(
selectedPoint.title!.firstWhere((t) => t.language == language).value!,
textStyle: TextStyle(fontSize: 20.0),
),
),
),
Row(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 15),
child: Column(
children: [
Container(
height: isPointPrice && selectedPoint.prices!.firstWhere((d) => d.language == language).value!.length > breakPointPrice ? size.height * 0.50 : size.height * 0.7,
width: size.width * 0.38,
decoration: BoxDecoration(
color: kBackgroundLight,
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0))
),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 10, bottom: 10, top: 15),
child: Scrollbar(
controller: scrollDescription,
thumbVisibility: true,
thickness: 2.0,
child: SingleChildScrollView(
controller: scrollDescription,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
if(selectedPoint.description!.any((d) => d.language == language) && selectedPoint.description!.firstWhere((d) => d.language == language).value != null && selectedPoint.description!.firstWhere((d) => d.language == language).value!.trim().length > 0)
HtmlWidget(
selectedPoint.description!.firstWhere((d) => d.language == language).value!,
customStylesBuilder: (element) {
return {'text-align': 'left', 'font-family': "Roboto"};
},
textStyle: TextStyle(fontSize: kDescriptionSize),
),
],
),
),
),
),
),
),
if(isPointPrice && selectedPoint.prices!.firstWhere((d) => d.language == language).value!.length > breakPointPrice)
Container(
height: size.height * 0.20,
width: size.width * 0.38,
decoration: BoxDecoration(
color: kBackgroundLight,
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0))
),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 10, bottom: 10, top: 15),
child: Scrollbar(
controller: scrollPrice,
thumbVisibility: true,
thickness: 2.0,
child: SingleChildScrollView(
controller: scrollPrice,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
//crossAxisAlignment: CrossAxisAlignment.center,
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(Icons.price_change_outlined, color: primaryColor, size: 25),
)),
HtmlWidget(
selectedPoint.prices!.firstWhere((d) => d.language == language).value!,
customStylesBuilder: (element) {
return {'text-align': 'left', 'font-family': "Roboto"};
},
textStyle: TextStyle(fontSize: kDescriptionSize),
),
],
),
),
),
),
),
),
],
),
),
SizedBox(
width: size.width * 0.32,
height: size.height * 0.7,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
//color: Colors.green,
height: size.height * 0.35,
child: CarouselSlider(
carouselController: sliderController,
options: CarouselOptions(
onPageChanged: (int index, CarouselPageChangedReason reason) {
currentIndex.value = index + 1;
},
height: size.height *0.33,
enlargeCenterPage: true,
pageSnapping: true,
reverse: false,
),
items: selectedPoint.contents!.map<Widget>((ContentDTO i) {
return Builder(
builder: (BuildContext context) {
AppContext appContext = Provider.of<AppContext>(context);
var resourcetoShow = getElementForResource(context, appContext, i, true);
return resourcetoShow;
},
);
}).toList(),
),
),
Container(
//color: Colors.yellow,
height: size.height * 0.33,
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Column(
children: [
/*if(isPointPrice && selectedPoint.prices!.firstWhere((d) => d.language == language).value!.length > breakPointPrice)
Container(
height: size.height * 0.1,
width: size.width * 0.38,
decoration: BoxDecoration(
color: kBackgroundLight,
borderRadius: BorderRadius.all(Radius.circular(tabletAppContext.configuration!.roundedValue?.toDouble() ?? 20.0))
),
child: Padding(
padding: EdgeInsets.all(5),
child: Scrollbar(
thumbVisibility: true,
thickness: 2.0,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(5.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
// todo add width ?
Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(Icons.price_change_outlined, color: primaryColor, size: 13),
),
HtmlWidget(
selectedPoint.prices!.firstWhere((d) => d.language == language).value!,
customStylesBuilder: (element) {
return {'text-align': 'left', 'font-family': "Roboto"};
},
textStyle: TextStyle(fontSize: 12),
),
],
),
),
),
),
),
),*/
if(isPointPrice && selectedPoint.prices!.firstWhere((d) => d.language == language).value!.length <= breakPointPrice)
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(Icons.price_change_outlined, color: primaryColor, size: 13),
Padding(
padding: const EdgeInsets.all(4.0),
child: HtmlWidget(
selectedPoint.prices!.firstWhere((d) => d.language == language).value!,
customStylesBuilder: (element) {
return {'text-align': 'left', 'font-family': "Roboto"};
},
textStyle: TextStyle(fontSize: 12),
),
)
],
),
selectedPoint.phone != null && selectedPoint.phone!.isNotEmpty && selectedPoint.phone!.any((d) => d.language == language) && selectedPoint.phone!.firstWhere((d) => d.language == language).value != null && selectedPoint.phone!.firstWhere((d) => d.language == language).value!.trim().length > 0 ? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(Icons.phone, color: primaryColor, size: 13),
Padding(
padding: const EdgeInsets.all(4.0),
child: Text(parse(selectedPoint.phone!.firstWhere((p) => p.language == language).value!).documentElement!.text, style: TextStyle(fontSize: 12)),
)
],
): SizedBox(),
selectedPoint.email != null && selectedPoint.email!.isNotEmpty && selectedPoint.email!.any((d) => d.language == language) && selectedPoint.email!.firstWhere((d) => d.language == language).value != null && selectedPoint.email!.firstWhere((d) => d.language == language).value!.trim().length > 0 ? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(Icons.email, color: primaryColor, size: 13),
Padding(
padding: const EdgeInsets.all(4.0),
child: SizedBox(
width: size.width*0.25,
child: AutoSizeText(parse(selectedPoint.email!.firstWhere((p) => p.language == language).value!).documentElement!.text, style: TextStyle(fontSize: 12), maxLines: 3)
),
)
],
): SizedBox(),
selectedPoint.site != null && selectedPoint.site!.isNotEmpty && selectedPoint.site!.any((d) => d.language == language) && selectedPoint.site!.firstWhere((d) => d.language == language).value != null && selectedPoint.site!.firstWhere((d) => d.language == language).value!.trim().length > 0 ? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(Icons.public, color: primaryColor, size: 13),
Padding(
padding: const EdgeInsets.all(4.0),
child: SizedBox(
width: size.width*0.25,
child: AutoSizeText(parse(selectedPoint.site!.firstWhere((p) => p.language == language).value!).documentElement!.text, style: TextStyle(fontSize: 12), maxLines: 3)
),
)
],
): SizedBox(),
selectedPoint.site != null && selectedPoint.site!.isNotEmpty && selectedPoint.site!.any((d) => d.language == language) && selectedPoint.site!.firstWhere((d) => d.language == language).value != null && selectedPoint.site!.firstWhere((d) => d.language == language).value!.trim().length > 0 ? Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: size.width *0.1,
height: 120,
child: QrImageView(
padding: EdgeInsets.only(left: 5.0, top: 5.0, bottom: 5.0, right: 5.0),
data: parse(selectedPoint.site!.firstWhere((p) => p.language == language).value!).documentElement!.text,
version: QrVersions.auto,
size: 50.0,
),
),
),
),
): SizedBox(),
],
),
),
),
],
),
),
)
],
)
],
),
],
),
/*Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: HtmlWidget(
(mapContext.getSelectedMarker() as MapMarker).title!,
customStylesBuilder: (element) {
return {'text-align': 'center'};
},
textStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: kIsWeb ? kWebTitleSize : kTitleSize)
),
),
),
Padding(
padding: const EdgeInsets.only(top: 75),
child: Center(
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if((mapContext.getSelectedMarker() as MapMarker).contents != null && (mapContext.getSelectedMarker() as MapMarker).contents!.length > 0)
Stack(
children: [
Container(
//color: Colors.green,
child: CarouselSlider(
carouselController: sliderController,
options: CarouselOptions(
onPageChanged: (int index, CarouselPageChangedReason reason) {
currentIndex.value = index + 1;
},
height: size.height *0.35,
enlargeCenterPage: true,
pageSnapping: true,
reverse: false,
),
items: (mapContext.getSelectedMarker() as MapMarker).contents!.map<Widget>((ContentGeoPoint i) {
return Builder(
builder: (BuildContext context) {
AppContext appContext = Provider.of<AppContext>(context);
var resourcetoShow = getElementForResource(context, appContext, i);
return Padding(
padding: const EdgeInsets.all(8.0),
child: resourcetoShow,
);
},
);
}).toList(),
),
),
Positioned(
bottom: 0,
child: Container(
//color: Colors.red,
height: 25,
width: sizeMarker.width,
//color: Colors.amber,
child: Padding(
padding: const EdgeInsets.only(top: 0),
child: Align(
alignment: Alignment.bottomCenter,
child: ValueListenableBuilder<int>(
valueListenable: currentIndex,
builder: (context, value, _) {
return Text(
value.toString()+'/'+(mapContext.getSelectedMarker() as MapMarker).contents!.length.toString(),
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
);
}
)
),
),
),
),
],
),
// Description
Container(
height: (mapContext.getSelectedMarker() as MapMarker).contents != null && (mapContext.getSelectedMarker() as MapMarker).contents!.length > 0 ? size.height *0.3 : size.height *0.6,
width: MediaQuery.of(context).size.width *0.4,
decoration: BoxDecoration(
color: kBackgroundColor,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(tabletAppContext.configuration!.roundedValue?.toDouble() ?? 10.0),
boxShadow: [
BoxShadow(
color: kBackgroundSecondGrey,
spreadRadius: 0.5,
blurRadius: 1.1,
offset: Offset(0, 1.1), // changes position of shadow
),
],
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: HtmlWidget(
(mapContext.getSelectedMarker() as MapMarker).description!,
customStylesBuilder: (element) {
return {'text-align': 'center'};
},
textStyle: TextStyle(fontSize: kIsWeb ? kWebDescriptionSize : kDescriptionSize)
),
),
),
),
]
),
if((mapContext.getSelectedMarker() as MapMarker).contents != null && (mapContext.getSelectedMarker() as MapMarker).contents!.length > 1)
Positioned(
top: MediaQuery.of(context).size.height * 0.125,
right: -10,
child: InkWell(
onTap: () {
if ((mapContext.getSelectedMarker() as MapMarker).contents!.length > 0)
sliderController!.nextPage(duration: new Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
},
child: Icon(
Icons.chevron_right,
size: kIsWeb ? 100 : 85,
color: kTestSecondColor,
),
)
),
if((mapContext.getSelectedMarker() as MapMarker).contents != null && (mapContext.getSelectedMarker() as MapMarker).contents!.length > 1)
Positioned(
top: MediaQuery.of(context).size.height * 0.125,
left: -10,
child: InkWell(
onTap: () {
if ((mapContext.getSelectedMarker() as MapMarker).contents!.length > 0)
sliderController!.previousPage(duration: new Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
},
child: Icon(
Icons.chevron_left,
size: kIsWeb ? 100 : 85,
color: kTestSecondColor,
),
)
),
],
),
),
),*/
])
),
),
);
}
}
getElementForResource(BuildContext context, AppContext appContext, ContentDTO i, bool addFullScreen) {
var widgetToInclude;
Size size = MediaQuery.of(context).size;
VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext;
switch(i.resource?.type) {
case ResourceType.Image:
case ResourceType.ImageUrl:
widgetToInclude = GestureDetector(
onTap: () {
if(addFullScreen) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0))
),
contentPadding: EdgeInsets.zero,
// title: Text(eventAgenda.name!),
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0)),
color: kBackgroundColor,
),
height: size.height * 0.8,
width: size.width * 0.8,
child: Container(
decoration: BoxDecoration(
//color: Colors.yellow,
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0)),
),
child: PhotoView(
imageProvider: ImageCustomProvider.getImageProvider(appContext, i.resourceId!, i.resource!.url!),
minScale: PhotoViewComputedScale.contained * 0.8,
maxScale: PhotoViewComputedScale.contained * 3.0,
backgroundDecoration: BoxDecoration(
color: kBackgroundGrey,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
),
),
),
),
);
},
);
}
},
child: Container(
decoration: BoxDecoration(
//color: kBackgroundLight,
image: DecorationImage(
image: ImageCustomProvider.getImageProvider(appContext, i.resourceId!, i.resource!.url!),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0)),
/*border: Border.all(
color: kBackgroundGrey,
width: 1.0,
),*/
),
),
);
break;
case ResourceType.Video:
case ResourceType.VideoUrl:
case ResourceType.Audio:
widgetToInclude = GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
if(addFullScreen && i.resource!.type != ResourceType.Audio) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0))
),
contentPadding: EdgeInsets.zero,
// title: Text(eventAgenda.name!),
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 20.0)),
color: kBackgroundColor,
),
height: size.height * 0.8,
width: size.width * 0.8,
child: Center(child: showElementForResource(ResourceDTO(id: i.resourceId, url: i.resource?.url, type: i.resource?.type), appContext, false, true)),
),
);
},
);
}
},
child: IgnorePointer(
ignoring: i.resource!.type != ResourceType.Audio,
child: Container(
decoration: BoxDecoration(
color: Colors.yellow,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
),
child: showElementForResource(ResourceDTO(id: i.resourceId, url: i.resource?.url, type: i.resource?.type), appContext, false, true),
),
),
);
break;
}
return Center(
child: Container(
height: MediaQuery.of(context).size.height * 0.6,
width: MediaQuery.of(context).size.width * 0.72,
child: AspectRatio(
aspectRatio: 16 / 9,
child: ClipRect(
child: widgetToInclude,
),
),
),
);
}

View File

@ -0,0 +1,42 @@
class TreeNode {
bool checked;
bool show;
int id;
int pid;
int commonID;
String title;
List<TreeNode> children;
TreeNode({
required this.checked,
required this.show,
required this.id,
required this.pid,
required this.commonID,
required this.title,
required this.children,
});
factory TreeNode.fromJson(Map<String, dynamic> json) {
return TreeNode(
checked: json['checked'] ?? false,
show: json['show'] ?? false,
id: json['id'] ?? 0,
pid: json['pid'] ?? 0,
commonID: json['commonID'] ?? 0,
title: json['title'] ?? '',
children: (json['children'] as List<dynamic>)
.map((childJson) => TreeNode.fromJson(childJson))
.toList(),
);
}
List<int> getAllChildrenTitles() {
List<int> id = [];
for (var child in children) {
id.add(child.id);
id.addAll(child.getAllChildrenTitles());
}
return id;
}
}

View File

@ -55,14 +55,21 @@ class _SliderPage extends State<SliderPage> {
return Stack( return Stack(
children: [ children: [
Container(
height: size.height,
width: size.width,
color: kBackgroundLight,
),
if(sliderDTO.contents != null && sliderDTO.contents!.isNotEmpty) if(sliderDTO.contents != null && sliderDTO.contents!.isNotEmpty)
CarouselSlider( Center(
child: CarouselSlider(
carouselController: sliderController, carouselController: sliderController,
options: CarouselOptions( options: CarouselOptions(
onPageChanged: (int index, CarouselPageChangedReason reason) { onPageChanged: (int index, CarouselPageChangedReason reason) {
currentIndex.value = index + 1; currentIndex.value = index + 1;
}, },
height: MediaQuery.of(context).size.height * 1.0, enableInfiniteScroll: false,
height: MediaQuery.of(context).size.height * 0.92,
enlargeCenterPage: false, enlargeCenterPage: false,
reverse: false, reverse: false,
), ),
@ -74,7 +81,7 @@ class _SliderPage extends State<SliderPage> {
height: MediaQuery.of(context).size.height, height: MediaQuery.of(context).size.height,
margin: const EdgeInsets.symmetric(horizontal: 5.0), margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.green, color: kBackgroundGrey,
//color: configurationDTO.imageId == null ? configurationDTO.secondaryColor != null ? new Color(int.parse(configurationDTO.secondaryColor!.split('(0x')[1].split(')')[0], radix: 16)): kBackgroundGrey : null, //color: configurationDTO.imageId == null ? configurationDTO.secondaryColor != null ? new Color(int.parse(configurationDTO.secondaryColor!.split('(0x')[1].split(')')[0], radix: 16)): kBackgroundGrey : null,
borderRadius: BorderRadius.circular(visitAppContex.configuration!.roundedValue?.toDouble() ?? 10.0), borderRadius: BorderRadius.circular(visitAppContex.configuration!.roundedValue?.toDouble() ?? 10.0),
//border: Border.all(width: 0.3, color: kSecondGrey), //border: Border.all(width: 0.3, color: kSecondGrey),
@ -86,7 +93,7 @@ class _SliderPage extends State<SliderPage> {
Padding( Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: Container( child: Container(
color: Colors.orange, //color: Colors.orange,
height: MediaQuery.of(context).size.height * 0.6, height: MediaQuery.of(context).size.height * 0.6,
width: MediaQuery.of(context).size.width * 0.72, width: MediaQuery.of(context).size.width * 0.72,
/*decoration: BoxDecoration( /*decoration: BoxDecoration(
@ -133,7 +140,7 @@ class _SliderPage extends State<SliderPage> {
height: MediaQuery.of(context).size.height *0.25, height: MediaQuery.of(context).size.height *0.25,
width: MediaQuery.of(context).size.width *0.7, width: MediaQuery.of(context).size.width *0.7,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue,// kBackgroundLight, color: kBackgroundLight,// kBackgroundLight,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(visitAppContex.configuration!.roundedValue?.toDouble() ?? 10.0), borderRadius: BorderRadius.circular(visitAppContex.configuration!.roundedValue?.toDouble() ?? 10.0),
boxShadow: const [ boxShadow: const [
@ -167,6 +174,7 @@ class _SliderPage extends State<SliderPage> {
); );
}).toList(), }).toList(),
), ),
),
/*if(sliderDTO.contents != null && sliderDTO.contents!.length > 1) /*if(sliderDTO.contents != null && sliderDTO.contents!.length > 1)
Positioned( Positioned(
top: MediaQuery.of(context).size.height * 0.35, top: MediaQuery.of(context).size.height * 0.35,
@ -214,7 +222,7 @@ class _SliderPage extends State<SliderPage> {
return AnimatedSmoothIndicator( return AnimatedSmoothIndicator(
activeIndex: value -1, activeIndex: value -1,
count: sliderDTO.contents!.length, count: sliderDTO.contents!.length,
effect: const ExpandingDotsEffect(activeDotColor: kMainColor), effect: ExpandingDotsEffect(activeDotColor: primaryColor!),
); );
/*Text( /*Text(
@ -287,8 +295,16 @@ class _SliderPage extends State<SliderPage> {
minScale: PhotoViewComputedScale.contained * 0.8, minScale: PhotoViewComputedScale.contained * 0.8,
maxScale: PhotoViewComputedScale.contained * 3.0, maxScale: PhotoViewComputedScale.contained * 3.0,
backgroundDecoration: BoxDecoration( backgroundDecoration: BoxDecoration(
color: kBackgroundSecondGrey, color: kBackgroundLight,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
boxShadow: const [
BoxShadow(
color: kBackgroundGrey,
spreadRadius: 0.3,
blurRadius: 4,
offset: Offset(0, 2), // changes position of shadow
),
],
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0), borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
), ),
); );
@ -299,8 +315,16 @@ class _SliderPage extends State<SliderPage> {
minScale: PhotoViewComputedScale.contained * 0.8, minScale: PhotoViewComputedScale.contained * 0.8,
maxScale: PhotoViewComputedScale.contained * 3.0, maxScale: PhotoViewComputedScale.contained * 3.0,
backgroundDecoration: BoxDecoration( backgroundDecoration: BoxDecoration(
color: kBackgroundSecondGrey, color: kBackgroundLight,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
boxShadow: const [
BoxShadow(
color: kBackgroundGrey,
spreadRadius: 0.3,
blurRadius: 4,
offset: Offset(0, 2), // changes position of shadow
),
],
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0), borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
), ),
); );
@ -310,7 +334,7 @@ class _SliderPage extends State<SliderPage> {
case ResourceType.Audio: case ResourceType.Audio:
widgetToInclude = Container( widgetToInclude = Container(
decoration: BoxDecoration( decoration: BoxDecoration(
//color: kBackgroundSecondGrey, color: kBackgroundLight,
//shape: BoxShape.rectangle, //shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0), borderRadius: BorderRadius.circular(visitAppContext.configuration!.roundedValue?.toDouble() ?? 15.0),
), ),
@ -323,7 +347,6 @@ class _SliderPage extends State<SliderPage> {
child: Container( child: Container(
height: MediaQuery.of(context).size.height * 0.6, height: MediaQuery.of(context).size.height * 0.6,
width: MediaQuery.of(context).size.width * 0.72, width: MediaQuery.of(context).size.width * 0.72,
color: Colors.yellow,
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: ClipRect( child: ClipRect(

View File

@ -256,6 +256,7 @@ class _BodyState extends State<Body> {
rawSections = jsonDecode(jsonEncode(sectionsDownloaded)); rawSections = jsonDecode(jsonEncode(sectionsDownloaded));
var rawToSection = jsonDecode(jsonEncode(rawSections)).map((json) => SectionDTO.fromJson(json)).toList(); var rawToSection = jsonDecode(jsonEncode(rawSections)).map((json) => SectionDTO.fromJson(json)).toList();
List<SectionDTO> sectionList = rawToSection.whereType<SectionDTO>().toList(); List<SectionDTO> sectionList = rawToSection.whereType<SectionDTO>().toList();
visitAppContext.currentSections = rawSections;
//print(sectionsDownloaded); //print(sectionsDownloaded);
if(sectionList.isNotEmpty) { if(sectionList.isNotEmpty) {

View File

@ -78,6 +78,7 @@ class _VisitPageState extends State<VisitPage> with WidgetsBindingObserver {
final appContext = Provider.of<AppContext>(context, listen: false); final appContext = Provider.of<AppContext>(context, listen: false);
VisitAppContext visitAppContext = appContext.getContext(); VisitAppContext visitAppContext = appContext.getContext();
visitAppContext.configuration = widget.configuration; visitAppContext.configuration = widget.configuration;
visitAppContext.sectionIds = widget.configuration.sectionIds;
appContext.setContext(visitAppContext); appContext.setContext(visitAppContext);
}); });
//listeningState(); //listeningState();

View File

@ -1,10 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
//import 'package:confetti/confetti.dart'; //import 'package:confetti/confetti.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:manager_api_new/api.dart'; import 'package:manager_api_new/api.dart';
import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart';
import 'package:mymuseum_visitapp/Components/loading_common.dart'; import 'package:mymuseum_visitapp/Components/loading_common.dart';
@ -14,16 +15,21 @@ import 'package:mymuseum_visitapp/Models/articleRead.dart';
import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Article/article_page.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/Map/map_context.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Map/map_page.dart';
import 'package:mymuseum_visitapp/Screens/Sections/PDF/pdf_page.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Puzzle/puzzle_page.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Puzzle/puzzle_page.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Quiz/quizz_page.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Quiz/quizz_page.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Slider/slider_view.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Slider/slider_page.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Video/video_page.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Video/video_page.dart';
import 'package:mymuseum_visitapp/Screens/Sections/Web/web_page.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Web/web_page.dart';
import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/app_context.dart';
import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/client.dart';
import 'package:mymuseum_visitapp/constants.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
import 'package:image/image.dart' as IMG;
import 'dart:ui' as ui;
import 'package:path_provider/path_provider.dart';
class SectionPage extends StatefulWidget { class SectionPage extends StatefulWidget {
const SectionPage({Key? key, required this.rawSection, required this.visitAppContextIn, required this.configuration, required this.sectionId}) : super(key: key); const SectionPage({Key? key, required this.rawSection, required this.visitAppContextIn, required this.configuration, required this.sectionId}) : super(key: key);
@ -44,6 +50,9 @@ class _SectionPageState extends State<SectionPage> {
late dynamic rawSectionData; late dynamic rawSectionData;
List<ResourceModel?> resourcesModel = <ResourceModel?>[]; List<ResourceModel?> resourcesModel = <ResourceModel?>[];
late final MapContext mapContext = MapContext(null);
List<Map<String, dynamic>>? icons;
@override @override
void initState() { void initState() {
widget.visitAppContextIn.isContentCurrentlyShown = true; widget.visitAppContextIn.isContentCurrentlyShown = true;
@ -66,11 +75,15 @@ class _SectionPageState extends State<SectionPage> {
return Scaffold( return Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
appBar: test!.type != SectionType.Quiz && test.type != SectionType.Article && test.type != SectionType.Web && test.type != SectionType.Pdf && test.type != SectionType.Video && test.type != SectionType.Puzzle && test.type != SectionType.Slider ? CustomAppBar( resizeToAvoidBottomInset: false,
appBar: test!.type == SectionType.Menu || test.type == SectionType.Agenda || test.type == SectionType.Weather ? CustomAppBar(
title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "", title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "",
isHomeButton: false, isHomeButton: false,
) : null, ) : null,
body: OrientationBuilder( body: MediaQuery.removeViewInsets(
context: context,
removeBottom: true,
child: OrientationBuilder(
builder: (context, orientation) { builder: (context, orientation) {
return FutureBuilder( return FutureBuilder(
future: getSectionDetail(appContext, visitAppContext.clientAPI, widget.sectionId), future: getSectionDetail(appContext, visitAppContext.clientAPI, widget.sectionId),
@ -99,6 +112,12 @@ class _SectionPageState extends State<SectionPage> {
case SectionType.Slider: case SectionType.Slider:
SliderDTO sliderDTO = SliderDTO.fromJson(sectionResult)!; SliderDTO sliderDTO = SliderDTO.fromJson(sectionResult)!;
return SliderPage(section: sliderDTO); return SliderPage(section: sliderDTO);
case SectionType.Map:
MapDTO mapDTO = MapDTO.fromJson(sectionResult)!;
return ChangeNotifierProvider<MapContext>.value(
value: mapContext,
child: MapPage(section: mapDTO, icons: icons ?? []),
);
default: default:
return const Center(child: Text("Unsupported type")); return const Center(child: Text("Unsupported type"));
} }
@ -108,6 +127,7 @@ class _SectionPageState extends State<SectionPage> {
} }
); );
} }
),
) )
); );
} }
@ -219,6 +239,10 @@ class _SectionPageState extends State<SectionPage> {
} }
} }
break; break;
case SectionType.Map:
MapDTO mapDTO = MapDTO.fromJson(rawSectionData)!;
icons = await getByteIcons(visitAppContext, mapDTO);
break;
default: default:
break; break;
} }
@ -232,3 +256,86 @@ class _SectionPageState extends State<SectionPage> {
} }
} }
} }
Future<Uint8List> 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;
}
Future<File?> _checkIfLocalResourceExists(VisitAppContext visitAppContext, String iconId) async {
try {
Directory? appDocumentsDirectory = Platform.isIOS ? await getApplicationDocumentsDirectory() : await getDownloadsDirectory();
String localPath = appDocumentsDirectory!.path;
Directory configurationDirectory = Directory('$localPath/${visitAppContext.configuration!.id}');
List<FileSystemEntity> fileList = configurationDirectory.listSync();
if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(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<List<Map<String, dynamic>>> getByteIcons(VisitAppContext visitAppContext, MapDTO mapDTO) async {
//var mapDTO = MapDTO.fromJson(jsonDecode(section.data!));
Uint8List 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(visitAppContext, 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<Map<String, dynamic>> 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(visitAppContext, 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;
}

View File

@ -17,14 +17,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.4.1" version: "6.4.1"
ar_flutter_plugin: archive:
dependency: "direct main" dependency: transitive
description: description:
name: ar_flutter_plugin name: archive
sha256: "52b6b2ccec4b624ca3fb8d7cc68128f11126580b412c3da2da82c41ddfd6d6ae" sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.3" version: "4.0.7"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -57,6 +57,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
benchmark:
dependency: transitive
description:
name: benchmark
sha256: cb3eeea01e3f054df76ee9775ca680f3afa5f19f39b2bb426ba78ba27654493b
url: "https://pub.dev"
source: hosted
version: "0.3.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -241,6 +249,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.0.8"
dart_earcut:
dependency: transitive
description:
name: dart_earcut
sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b
url: "https://pub.dev"
source: hosted
version: "1.2.0"
dart_sort_queue:
dependency: transitive
description:
name: dart_sort_queue
sha256: f3353ba8b4850e072d3368757f62edb79af34a9703c3e3df9c59342721f5f5b1
url: "https://pub.dev"
source: hosted
version: "0.0.2+3"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@ -395,6 +419,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_map:
dependency: "direct main"
description:
name: flutter_map
sha256: "2ecb34619a4be19df6f40c2f8dce1591675b4eff7a6857bd8f533706977385da"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
flutter_pdfview: flutter_pdfview:
dependency: "direct main" dependency: "direct main"
description: description:
@ -403,6 +435,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0+1" version: "1.4.0+1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "1c2b787f99bdca1f3718543f81d38aa1b124817dfeb9fb196201bea85b6134bf"
url: "https://pub.dev"
source: hosted
version: "2.0.26"
flutter_staggered_grid_view: flutter_staggered_grid_view:
dependency: "direct main" dependency: "direct main"
description: description:
@ -509,54 +549,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.15.4" version: "0.15.4"
geolocator: geotypes:
dependency: transitive dependency: transitive
description: description:
name: geolocator name: geotypes
sha256: "5c23f3613f50586c0bbb2b8f970240ae66b3bd992088cf60dd5ee2e6f7dde3a8" sha256: "5bedf57de92283133dd221e363812ef50eaaba414f0823b1974ef7d84b86991f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.0.2" version: "0.0.2"
geolocator_android:
dependency: transitive
description:
name: geolocator_android
sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47"
url: "https://pub.dev"
source: hosted
version: "4.6.1"
geolocator_apple:
dependency: transitive
description:
name: geolocator_apple
sha256: c4ecead17985ede9634f21500072edfcb3dba0ef7b97f8d7bc556d2d722b3ba3
url: "https://pub.dev"
source: hosted
version: "2.3.9"
geolocator_platform_interface:
dependency: transitive
description:
name: geolocator_platform_interface
sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
geolocator_web:
dependency: transitive
description:
name: geolocator_web
sha256: "102e7da05b48ca6bf0a5bda0010f886b171d1a08059f01bfe02addd0175ebece"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
geolocator_windows:
dependency: transitive
description:
name: geolocator_windows
sha256: "4f4218f122a6978d0ad655fa3541eea74c67417440b09f0657238810d5af6bdc"
url: "https://pub.dev"
source: hosted
version: "0.1.3"
get: get:
dependency: "direct main" dependency: "direct main"
description: description:
@ -573,6 +573,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.2"
google_maps:
dependency: transitive
description:
name: google_maps
sha256: "4d6e199c561ca06792c964fa24b2bac7197bf4b401c2e1d23e345e5f9939f531"
url: "https://pub.dev"
source: hosted
version: "8.1.1"
google_maps_flutter:
dependency: "direct main"
description:
name: google_maps_flutter
sha256: "621125e35e81ca39ef600e45243d2be93167e61def72bc7207b0c4a635c58506"
url: "https://pub.dev"
source: hosted
version: "2.10.1"
google_maps_flutter_android:
dependency: transitive
description:
name: google_maps_flutter_android
sha256: "721ffae2240e957c04b0de19ffd4b68580adb57a8224496b7fb55fad23aec98a"
url: "https://pub.dev"
source: hosted
version: "2.14.13"
google_maps_flutter_ios:
dependency: transitive
description:
name: google_maps_flutter_ios
sha256: c7433645c4c9b61c587938cb06072f3dad601239e596b090c0f8f206c1f2ade7
url: "https://pub.dev"
source: hosted
version: "2.15.2"
google_maps_flutter_platform_interface:
dependency: transitive
description:
name: google_maps_flutter_platform_interface
sha256: "970c8f766c02909c7be282dea923c971f83a88adaf07f8871d0aacebc3b07bb2"
url: "https://pub.dev"
source: hosted
version: "2.11.1"
google_maps_flutter_web:
dependency: transitive
description:
name: google_maps_flutter_web
sha256: a45786ea6691cc7cdbe2cf3ce2c2daf4f82a885745666b4a36baada3a4e12897
url: "https://pub.dev"
source: hosted
version: "0.5.12"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -613,6 +661,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
image:
dependency: "direct main"
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@ -677,6 +733,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.13" version: "0.4.13"
latlong2:
dependency: transitive
description:
name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.1"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -709,6 +773,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
lists:
dependency: transitive
description:
name: lists
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
logger:
dependency: transitive
description:
name: logger
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
url: "https://pub.dev"
source: hosted
version: "2.5.0"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -724,6 +804,14 @@ packages:
relative: true relative: true
source: path source: path
version: "1.0.0" version: "1.0.0"
mapbox_maps_flutter:
dependency: "direct main"
description:
name: mapbox_maps_flutter
sha256: f3dea7e14e5afc10ad03e16a6d5ece18ec5487ceaf9b2f83a64a2358d29149ba
url: "https://pub.dev"
source: hosted
version: "2.8.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -748,6 +836,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.15.0" version: "1.15.0"
mgrs_dart:
dependency: transitive
description:
name: mgrs_dart
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
url: "https://pub.dev"
source: hosted
version: "2.0.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -756,6 +852,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.5" version: "1.0.5"
mobile_scanner:
dependency: "direct main"
description:
name: mobile_scanner
sha256: "827765afbd4792ff3fd105ad593821ac0f6d8a7d352689013b07ee85be336312"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@ -956,6 +1060,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
polylabel:
dependency: transitive
description:
name: polylabel
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -964,6 +1076,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62
url: "https://pub.dev"
source: hosted
version: "6.0.2"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
url: "https://pub.dev"
source: hosted
version: "2.1.0"
provider: provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -988,14 +1116,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
qr_code_scanner: qr:
dependency: "direct main" dependency: transitive
description: description:
name: qr_code_scanner name: qr
sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.1" version: "3.0.2"
qr_flutter:
dependency: "direct main"
description:
name: qr_flutter
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
rbush:
dependency: transitive
description:
name: rbush
sha256: "48b683421b4afb43a642f82c6aa31911e54f3069143d31c7d33cbe329df13403"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
rxdart: rxdart:
dependency: transitive dependency: transitive
description: description:
@ -1004,6 +1148,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.27.7" version: "0.27.7"
sanitize_html:
dependency: transitive
description:
name: sanitize_html
sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
shared_preferences: shared_preferences:
dependency: transitive dependency: transitive
description: description:
@ -1161,6 +1313,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
sweepline_intersections:
dependency: transitive
description:
name: sweepline_intersections
sha256: a665c707200a4f07140a4029b41a7c4883beb3f04322cd8e08ebf650f69e1176
url: "https://pub.dev"
source: hosted
version: "0.0.4"
synchronized: synchronized:
dependency: transitive dependency: transitive
description: description:
@ -1193,6 +1353,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
turf:
dependency: transitive
description:
name: turf
sha256: "75347c45a5c1de805db7cb182286f05a3770e01546626c4dc292709d15cbe436"
url: "https://pub.dev"
source: hosted
version: "0.0.10"
turf_equality:
dependency: transitive
description:
name: turf_equality
sha256: f0f44ffe389547941358e0d3d4a747db2bd56115b32ff1cede5e5bdf3126a3e2
url: "https://pub.dev"
source: hosted
version: "0.1.0"
turf_pip:
dependency: transitive
description:
name: turf_pip
sha256: ba4fd414baffd5d7b30880658ad6db82461c49ec023f8ffd0c23d398ad8b14be
url: "https://pub.dev"
source: hosted
version: "0.0.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -1201,6 +1385,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.2"
unicode:
dependency: transitive
description:
name: unicode
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
url_launcher: url_launcher:
dependency: transitive dependency: transitive
description: description:
@ -1441,6 +1633,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.1" version: "5.5.1"
wkt_parser:
dependency: transitive
description:
name: wkt_parser
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -48,14 +48,16 @@ dependencies:
diacritic: ^0.1.3 diacritic: ^0.1.3
flutter_widget_from_html: ^0.15.2 flutter_widget_from_html: ^0.15.2
webview_flutter: ^4.10.0 webview_flutter: ^4.10.0
ar_flutter_plugin: ^0.7.3 #ar_flutter_plugin: ^0.7.3
flutter_pdfview: ^1.4.0+1 flutter_pdfview: ^1.4.0+1
youtube_player_flutter: ^9.1.1 youtube_player_flutter: ^9.1.1
youtube_player_iframe: ^5.2.1 youtube_player_iframe: ^5.2.1
# Specific mobile # Specific mobile
qr_code_scanner: ^1.0.1 #not in web #qr_code_scanner: ^1.0.1 #not in web
mobile_scanner: ^4.0.0 # that replace qr_code_scanner..
sqflite: #not in web sqflite: #not in web
just_audio_cache: ^0.1.2 #not in web just_audio_cache: ^0.1.2 #not in web
flutter_beacon: ^0.5.1 #not in web flutter_beacon: ^0.5.1 #not in web
@ -63,6 +65,12 @@ dependencies:
smooth_page_indicator: ^1.2.1 smooth_page_indicator: ^1.2.1
mapbox_maps_flutter: ^2.0.0 # specific mobile ..
google_maps_flutter: ^2.5.3 # Specific mobile and web
qr_flutter: ^4.1.0 # multi
flutter_map: ^7.0.2 #all
image: ^4.1.7
manager_api_new: manager_api_new:
path: manager_api_new path: manager_api_new
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.