import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:mymuseum_visitapp/Helpers/instanceImages.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; /// Le loader lui-même, sans rien savoir du contexte : le splash s'affiche avant /// que le `Provider` n'existe et ne peut donc pas passer par `LoadingCommon`. /// /// L'image vient du manager (Applications → Mobile). Tant qu'elle n'est pas /// connue, on tourne sur l'indicateur natif plutôt que sur `assets/loader/`, qui /// ne contient que des PNG 1x1 transparents — invisibles, et `errorBuilder` ne /// se déclenche pas puisque le fichier est valide. class RotatingLoader extends StatefulWidget { const RotatingLoader({Key? key, this.file, this.imageUrl, this.size, this.color}) : super(key: key); final File? file; final String? imageUrl; final double? size; final Color? color; @override State createState() => _RotatingLoaderState(); } class _RotatingLoaderState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller = AnimationController( duration: const Duration(seconds: 3), vsync: this, )..repeat(); @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final size = widget.size ?? MediaQuery.of(context).size.height * 0.1; final color = widget.color ?? kMainColor1; Widget? image; if (widget.file != null) { image = Image.file(widget.file!, width: size, height: size, fit: BoxFit.contain); } else if (widget.imageUrl != null) { image = CachedNetworkImage( imageUrl: widget.imageUrl!, width: size, height: size, fit: BoxFit.contain, progressIndicatorBuilder: (context, url, progress) => CircularProgressIndicator(value: progress.progress, color: color), errorWidget: (context, url, error) => CircularProgressIndicator(color: color), ); } if (image == null) { return SizedBox( width: size, height: size, child: Center(child: CircularProgressIndicator(color: color)), ); } return SizedBox( width: size, height: size, child: RotationTransition(turns: _controller, child: image), ); } } /// Loader des écrans de l'app. Ordre de préférence, celui de `visitapp-web` : /// l'image de la visite si elle en définit une, sinon celle de l'instance mise /// en cache au démarrage. class LoadingCommon extends StatelessWidget { const LoadingCommon({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final appContext = Provider.of(context); final VisitAppContext visitAppContext = appContext.getContext(); final configuration = visitAppContext.configuration; final primaryColor = configuration?.primaryColor != null ? Color(int.parse( configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : kMainColor1; return Center( child: FutureBuilder( future: InstanceImages.restore(), builder: (context, _) { final localConfigLoader = configuration?.loaderImageId != null ? _localResource(visitAppContext, configuration!.loaderImageId!) : null; return RotatingLoader( file: localConfigLoader ?? InstanceImages.loader, imageUrl: localConfigLoader == null ? configuration?.loaderImageUrl : null, color: primaryColor, ); }, ), ); } } /// Fichier déjà téléchargé pour la visite en cours, s'il existe. Lecture /// synchrone : le répertoire est local et l'écran de chargement ne peut pas se /// permettre une frame vide pour aller le vérifier. File? _localResource(VisitAppContext visitAppContext, String resourceId) { try { if (visitAppContext.localPath == null || visitAppContext.configuration?.id == null) { return null; } final directory = Directory('${visitAppContext.localPath}/${visitAppContext.configuration!.id}'); if (!directory.existsSync()) return null; for (final entity in directory.listSync()) { if (entity.uri.pathSegments.last.contains(resourceId)) return File(entity.path); } } catch (e) { debugPrint('[loader] ressource locale illisible : $e'); } return null; }