Premier vrai build depuis avril. Le diagnostic des docs — « un seul défaut, purement Gradle, peut-être réglé par 5a6701d » — tenait parce que personne n'avait lancé la commande. K1 — dix crans d'outillage, chacun dicté par l'erreur du précédent : Gradle 7.5 → 8.11.1, AGP 7.2.0 → 8.9.1, Kotlin 1.9.0 → 2.3.10, enableUncompressedNativeLibs retirée (supprimée en AGP 8.1), jcenter → mavenCentral, heap 1536M → 4096M, Jetifier coupé, et retrait du resolutionStrategy qui forçait androidx.lifecycle à 2.4.0 « to fix mapbox issue » : il réglait un problème d'il y a trois ans et causait celui d'aujourd'hui, mapbox_maps_flutter 2.21.1 appelant setViewTreeLifecycleOwner. Les trois derniers crans étaient de simples alignements sur mymuseum-visitapp, qui utilise le même plugin sans ces problèmes. Le bloc buildscript commenté de android/build.gradle est supprimé : il déclarait une config qui n'était pas celle appliquée et a fait conclure deux fois à tort. K2 — les 132 erreurs Dart que le Gradle masquait. Trois causes : roundedValue, isDate, isHour, isSectionImageBackground et screenPercentageSectionsMainPage ont migré vers AppConfigurationLink ; GeoPointDTO.latitude/longitude sont devenus geometry ; le reste en découlait. Deux bugs latents trouvés au passage : - applicationInstanceDTO n'était assigné que si l'instance avait l'IA — il servait de drapeau d'assistant. Ce DTO portant désormais les AppConfigurationLink, les cinq réglages seraient retombés sur leurs défauts sur MDLF et le Fort, sans erreur ni log. Drapeau rendu explicite, en préservant le ET instance × canal. - ConfigurationDTO.isTablet ayant disparu, le filtre des configurations tablette n'avait plus de source : il porte sur les liens du canal AppType.Tablet. La formule de clé par coordonnées de geo_point_filter, dupliquée à quatre endroits alors que les deux côtés de l'appariement doivent produire la même valeur, ne vit plus qu'à un seul (Helpers/geo.dart). Ce fichier documente aussi les deux conventions de coordonnées du projet : [lng, lat] pour les GeoPoint, [lat, lng] pour les géométries de GuidedStep. K4 — l'écran Game repris de mymuseum-visitapp, Screens/Puzzle supprimé. La tablette gagne le puzzle glissant, le bouton d'indice et un dimensionnement au ratio de l'image. Les couleurs, codées en dur par flavor client chez mymuseum, sont dérivées de configuration.primaryColor : tablet-app n'a pas de flavor, un seul APK sert tous les clients. Reste à vérifier sur device : le rendu de l'écran Game, et l'écran de sélection de configuration, qui sera vide si les configurations ne sont pas rattachées au canal tablette. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
417 lines
21 KiB
Dart
417 lines
21 KiB
Dart
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:auto_size_text/auto_size_text.dart';
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
|
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mapBox;
|
|
import 'package:manager_api_new/api.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:qr_flutter/qr_flutter.dart';
|
|
import 'package:tablet_app/Components/loading_common.dart';
|
|
import 'package:tablet_app/Components/video_viewer.dart';
|
|
import 'package:tablet_app/Components/video_viewer_youtube.dart';
|
|
import 'package:tablet_app/Models/agenda.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:tablet_app/Models/tabletContext.dart';
|
|
import 'package:tablet_app/app_context.dart';
|
|
import 'package:tablet_app/constants.dart';
|
|
|
|
class EventPopup extends StatefulWidget {
|
|
final EventAgenda eventAgenda;
|
|
final MapProvider mapProvider;
|
|
final Uint8List mapIcon;
|
|
|
|
EventPopup({Key? key, required this.eventAgenda, required this.mapProvider, required this.mapIcon}) : super(key: key);
|
|
|
|
@override
|
|
State<EventPopup> createState() => _EventPopupState();
|
|
}
|
|
|
|
class _EventPopupState extends State<EventPopup> {
|
|
final DateFormat formatter = DateFormat('dd/MM/yyyy hh:mm');
|
|
Completer<GoogleMapController> _controller = Completer();
|
|
Set<Marker> markers = {};
|
|
bool init = false;
|
|
|
|
mapBox.MapboxMap? mapboxMap;
|
|
mapBox.PointAnnotationManager? pointAnnotationManager;
|
|
|
|
Set<Marker> getMarkers() {
|
|
markers = {};
|
|
|
|
if (widget.eventAgenda.address!.lat != null && widget.eventAgenda.address!.lng != null) {
|
|
markers.add(Marker(
|
|
draggable: false,
|
|
markerId: MarkerId(widget.eventAgenda.address!.lat.toString() +
|
|
widget.eventAgenda.address!.lng.toString()),
|
|
position: LatLng(
|
|
double.parse(widget.eventAgenda.address!.lat!.toString()),
|
|
double.parse(widget.eventAgenda.address!.lng!.toString()),
|
|
),
|
|
icon: BitmapDescriptor.defaultMarker,
|
|
infoWindow: InfoWindow.noText));
|
|
}
|
|
return markers;
|
|
}
|
|
|
|
_onMapCreated(mapBox.MapboxMap mapboxMap, Uint8List icon) {
|
|
this.mapboxMap = mapboxMap;
|
|
|
|
mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async {
|
|
this.pointAnnotationManager = pointAnnotationManager;
|
|
pointAnnotationManager.createMulti(createPoints(LatLng(double.parse(widget.eventAgenda.address!.lat!.toString()), double.parse(widget.eventAgenda.address!.lng!.toString())), icon));
|
|
init = true;
|
|
});
|
|
}
|
|
|
|
createPoints(LatLng position, Uint8List icon) {
|
|
var options = <mapBox.PointAnnotationOptions>[];
|
|
options.add(mapBox.PointAnnotationOptions(
|
|
geometry: mapBox.Point(
|
|
coordinates: mapBox.Position(
|
|
position.longitude,
|
|
position.latitude,
|
|
)), // .toJson()
|
|
iconSize: 1.3,
|
|
iconOffset: [0.0, 0.0],
|
|
symbolSortKey: 10,
|
|
iconColor: 0,
|
|
iconImage: null,
|
|
image: icon, //widget.selectedMarkerIcon,
|
|
));
|
|
print(options.length);
|
|
|
|
return options;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final appContext = Provider.of<AppContext>(context);
|
|
TabletAppContext tabletAppContext = appContext.getContext();
|
|
|
|
var dateToShow = widget.eventAgenda.dateFrom!.isAtSameMomentAs(widget.eventAgenda.dateTo!) ? "${formatter.format(widget.eventAgenda.dateFrom!)}": "${formatter.format(widget.eventAgenda.dateFrom!)} - ${formatter.format(widget.eventAgenda.dateTo!)}";
|
|
Size size = MediaQuery.of(context).size;
|
|
|
|
if(!init) {
|
|
print("getmarkers in build");
|
|
getMarkers();
|
|
init = true;
|
|
}
|
|
|
|
return AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(tabletAppContext.currentAppConfigurationLink?.roundedValue?.toDouble() ?? 20.0))
|
|
),
|
|
contentPadding: EdgeInsets.zero,
|
|
// title: Text(eventAgenda.name!),
|
|
content: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.all(Radius.circular(tabletAppContext.currentAppConfigurationLink?.roundedValue?.toDouble() ?? 20.0)),
|
|
color: kBackgroundColor,
|
|
),
|
|
height: size.height * 0.85,
|
|
child: Stack(
|
|
children: [
|
|
Positioned(
|
|
right: 5,
|
|
top: 5,
|
|
child: InkWell(
|
|
onTap: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
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,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
widget.eventAgenda.image != null ? ClipRRect(
|
|
borderRadius: BorderRadius.only(topLeft: Radius.circular(tabletAppContext.currentAppConfigurationLink?.roundedValue?.toDouble() ?? 20.0), bottomLeft: Radius.circular(tabletAppContext.currentAppConfigurationLink?.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: widget.eventAgenda.image!,
|
|
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.all(8.0),
|
|
child: Container(
|
|
//color: Colors.green,
|
|
height: size.height * 0.13,
|
|
width: size.width * 0.65,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(
|
|
height: size.height * 0.08,
|
|
child: HtmlWidget(
|
|
widget.eventAgenda.name!,
|
|
textStyle: TextStyle(fontSize: 23.0),
|
|
),/*AutoSizeText(
|
|
widget.eventAgenda.name!,
|
|
style: TextStyle(fontSize: 23),
|
|
maxFontSize: 23,
|
|
),*/
|
|
),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 4.0, left: 2.0, right: 4.0, bottom: 6.0),
|
|
child: Icon(Icons.calendar_today_rounded, color: kTestSecondColor, size: 18),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(2.0),
|
|
child: Text(dateToShow, style: TextStyle(fontSize: 18, color: kTestSecondColor, fontWeight: FontWeight.w500)),
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 15),
|
|
child: Container(
|
|
height: size.height * 0.65,
|
|
width: size.width * 0.38,
|
|
decoration: BoxDecoration(
|
|
color: kBackgroundLight,
|
|
borderRadius: BorderRadius.all(Radius.circular(tabletAppContext.currentAppConfigurationLink?.roundedValue?.toDouble() ?? 20.0))
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(left: 20, right: 10, bottom: 10, top: 15),
|
|
child: Scrollbar(
|
|
thumbVisibility: true,
|
|
thickness: 2.0,
|
|
child: SingleChildScrollView(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
HtmlWidget(
|
|
widget.eventAgenda.description!,
|
|
customStylesBuilder: (element) {
|
|
return {'text-align': 'left', 'font-family': "Roboto"};
|
|
},
|
|
textStyle: TextStyle(fontSize: kDescriptionSize),
|
|
),
|
|
if (widget.eventAgenda.idVideoYoutube != null && widget.eventAgenda.idVideoYoutube!.isNotEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: SizedBox(
|
|
height: 250,
|
|
width: 350,
|
|
child: VideoViewerYoutube(videoUrl: "https://www.youtube.com/watch?v=${widget.eventAgenda.idVideoYoutube}", isAuto: false, webView: true)
|
|
),
|
|
),
|
|
if (widget.eventAgenda.videoLink != null && widget.eventAgenda.videoLink!.isNotEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: SizedBox(
|
|
height: 250,
|
|
width: 350,
|
|
child: VideoViewer(videoUrl: widget.eventAgenda.videoLink!, file: null),
|
|
),
|
|
),
|
|
if (widget.eventAgenda.videoResourceUrl != null && widget.eventAgenda.videoResourceUrl!.isNotEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: SizedBox(
|
|
height: 250,
|
|
width: 350,
|
|
child: VideoViewer(videoUrl: widget.eventAgenda.videoResourceUrl!, file: null),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: size.width * 0.32,
|
|
height: size.height * 0.65,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
widget.eventAgenda.address!.lat != null && widget.eventAgenda.address!.lng != null ?
|
|
SizedBox(
|
|
width: size.width * 0.28,
|
|
height: size.height * 0.2,
|
|
child: widget.mapProvider == MapProvider.Google ?
|
|
GoogleMap(
|
|
mapToolbarEnabled: false,
|
|
initialCameraPosition: CameraPosition(
|
|
target: LatLng(double.parse(widget.eventAgenda.address!.lat!.toString()), double.parse(widget.eventAgenda.address!.lng!.toString())),
|
|
zoom: 14,
|
|
),
|
|
onMapCreated: (GoogleMapController controller) {
|
|
_controller.complete(controller);
|
|
},
|
|
markers: markers,
|
|
) :
|
|
mapBox.MapWidget(
|
|
key: ValueKey("mapBoxWidget"),
|
|
styleUri: mapBox.MapboxStyles.STANDARD,
|
|
onMapCreated: (maBoxMap) {
|
|
_onMapCreated(maBoxMap, widget.mapIcon);
|
|
},
|
|
cameraOptions: mapBox.CameraOptions(
|
|
center: mapBox.Point(coordinates: mapBox.Position(double.parse(widget.eventAgenda.address!.lng!.toString()), double.parse(widget.eventAgenda.address!.lat!.toString()))), // .toJson()
|
|
zoom: 14
|
|
),
|
|
),
|
|
): SizedBox(),
|
|
SizedBox(
|
|
width: size.width * 0.28,
|
|
height: size.height * 0.35,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
widget.eventAgenda.address!.address != null && widget.eventAgenda.address!.address!.isNotEmpty ? Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Icon(Icons.location_on, size: 13),
|
|
Padding(
|
|
padding: const EdgeInsets.all(4.0),
|
|
child: SizedBox(
|
|
width: size.width*0.25,
|
|
child: AutoSizeText(widget.eventAgenda.address!.address!, style: TextStyle(fontSize: 12), maxLines: 3)
|
|
),
|
|
)
|
|
],
|
|
): SizedBox(),
|
|
widget.eventAgenda.phone != null && widget.eventAgenda.phone!.isNotEmpty ? Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Icon(Icons.phone, size: 13),
|
|
Padding(
|
|
padding: const EdgeInsets.all(4.0),
|
|
child: Text(widget.eventAgenda.phone!, style: TextStyle(fontSize: 12)),
|
|
)
|
|
],
|
|
): SizedBox(),
|
|
widget.eventAgenda.email != null && widget.eventAgenda.email!.isNotEmpty ? Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Icon(Icons.email, size: 13),
|
|
Padding(
|
|
padding: const EdgeInsets.all(4.0),
|
|
child: SizedBox(
|
|
width: size.width*0.25,
|
|
child: AutoSizeText(widget.eventAgenda.email!, style: TextStyle(fontSize: 12), maxLines: 3)
|
|
),
|
|
)
|
|
],
|
|
): SizedBox(),
|
|
widget.eventAgenda.website != null && widget.eventAgenda.website!.isNotEmpty ? Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Icon(Icons.public, size: 13),
|
|
Padding(
|
|
padding: const EdgeInsets.all(4.0),
|
|
child: SizedBox(
|
|
width: size.width*0.25,
|
|
child: AutoSizeText(widget.eventAgenda.website!, style: TextStyle(fontSize: 12), maxLines: 3)
|
|
),
|
|
)
|
|
],
|
|
): SizedBox(),
|
|
widget.eventAgenda.website != null && widget.eventAgenda.website!.isNotEmpty ?
|
|
Expanded(
|
|
child: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Container(
|
|
width: size.width *0.1,
|
|
height: 125,
|
|
child: QrImageView(
|
|
padding: EdgeInsets.only(left: 5.0, top: 5.0, bottom: 5.0, right: 5.0),
|
|
data: widget.eventAgenda.website!,
|
|
version: QrVersions.auto,
|
|
size: 50.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
): SizedBox(),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
],
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|