From d237987889441674656f6e0ca2d9cfde60de8aaa Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Tue, 11 Aug 2026 15:31:07 +0200 Subject: [PATCH] =?UTF-8?q?Lot=20E=20:=20rendu=20PDF=20des=20m=C3=A9dias?= =?UTF-8?q?=20d'=C3=A9tape,=20et=20QuestionType=20nomm=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le cas PDF n'était traité nulle part : un PDF tombait dans le `default` et affichait "Not supported type". ⚠️ Le correctif ne va pas dans showElementForResource comme l'annonçait le plan : cette fonction fait un `return CachedCustomResource(...)` avant son switch, donc tout son switch est du code mort. Le vrai dispatcher est CachedCustomResource, et il en a deux — ressource distante et fichier local déjà téléchargé pour l'offline. Les deux sont traités. Le cas distant doit télécharger avant d'afficher, PDFView n'acceptant qu'un chemin de fichier. QuestionType : _kindOf comparait des entiers bruts, remplacé par les alias nommés du client. Co-Authored-By: Claude Opus 5 --- lib/Components/cached_custom_resource.dart | 52 +++++++++++++++++++ .../GuidedPath/guided_step_challenge.dart | 12 ++--- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/lib/Components/cached_custom_resource.dart b/lib/Components/cached_custom_resource.dart index 7ac7594..6a58774 100644 --- a/lib/Components/cached_custom_resource.dart +++ b/lib/Components/cached_custom_resource.dart @@ -1,8 +1,10 @@ import 'dart:io'; import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_pdfview/flutter_pdfview.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/audio_player.dart'; import 'package:mymuseum_visitapp/Components/video_viewer.dart'; @@ -74,6 +76,9 @@ class CachedCustomResource extends StatelessWidget { return VideoViewer(file: null, videoUrl: resourceDTO.url!); case ResourceType.Audio : return AudioPlayerFloatingContainer(file: null, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto); + case ResourceType.Pdf : + // PDFView ne lit qu'un fichier local : il faut télécharger d'abord. + return _RemotePdfView(url: resourceDTO.url!, progressColor: primaryColor); default: return const Text("Not supported type"); } @@ -89,6 +94,9 @@ class CachedCustomResource extends StatelessWidget { return VideoViewer(file: snapshot.data!, videoUrl: resourceDTO.url!); case ResourceType.Audio : return AudioPlayerFloatingContainer(file: snapshot.data!, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto); + case ResourceType.Pdf : + // Déjà téléchargé par la configuration hors ligne : lecture directe. + return PDFView(filePath: snapshot.data!.path); default: return const Text("Not supported type"); } @@ -124,3 +132,47 @@ class CachedCustomResource extends StatelessWidget { return appDocumentsDirectory!.path; } } + +/// Affiche un PDF distant. `PDFView` n'accepte qu'un chemin de fichier, donc le +/// document est d'abord écrit dans le dossier documents — même approche que +/// `PDFPage`, à laquelle ce widget évite de renvoyer pour un simple média d'étape. +class _RemotePdfView extends StatefulWidget { + final String url; + final Color progressColor; + + const _RemotePdfView({required this.url, required this.progressColor}); + + @override + State<_RemotePdfView> createState() => _RemotePdfViewState(); +} + +class _RemotePdfViewState extends State<_RemotePdfView> { + late final Future _file = _download(); + + Future _download() async { + final request = await HttpClient().getUrl(Uri.parse(widget.url)); + final response = await request.close(); + final bytes = await consolidateHttpClientResponseBytes(response); + final directory = await getApplicationDocumentsDirectory(); + final filename = widget.url.substring(widget.url.lastIndexOf("/") + 1); + final file = File("${directory.path}/$filename"); + return file.writeAsBytes(bytes, flush: true); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _file, + builder: (context, snapshot) { + if (snapshot.hasError) { + return const Center(child: Text("Error loading PDF")); + } + if (snapshot.data == null) { + return Center( + child: CircularProgressIndicator(color: widget.progressColor)); + } + return PDFView(filePath: snapshot.data!.path); + }, + ); + } +} diff --git a/lib/Screens/Sections/GuidedPath/guided_step_challenge.dart b/lib/Screens/Sections/GuidedPath/guided_step_challenge.dart index 1a928c9..6224f23 100644 --- a/lib/Screens/Sections/GuidedPath/guided_step_challenge.dart +++ b/lib/Screens/Sections/GuidedPath/guided_step_challenge.dart @@ -11,14 +11,10 @@ import 'package:mymuseum_visitapp/Screens/Sections/GuidedPath/guided_step_timer. enum _Kind { mcq, puzzle, simple } _Kind _kindOf(QuizQuestion q) { - switch (q.validationQuestionType?.value) { - case 2: - return _Kind.puzzle; - case 0: - return _Kind.simple; - default: - return _Kind.mcq; - } + final type = q.validationQuestionType; + if (type == QuestionType.puzzle) return _Kind.puzzle; + if (type == QuestionType.simple) return _Kind.simple; + return _Kind.mcq; } String _stripHtml(String html) => html