feat(tutorial): guide the first launch with spotlighted steps and gesture hands
Ajoute un didacticiel joué à la première utilisation : un voile sombre perce un trou de lumière autour de l'élément à découvrir, une main animée mime le geste attendu (tap, appui long, glisser, pincement à deux doigts pour zoomer) et une bulle explique l'étape avec sa progression. - visite d'accueil : nouvelle session, télémétrie, barre de navigation, réglages - visite de l'éditeur d'impacts : ajouter, déplacer, zoomer au pincement, valider - chaque visite n'est jouée qu'une fois (SharedPreferences) - Paramètres > Aide & didacticiel > Revoir le didacticiel : réinitialise tout et relance la visite au retour sur l'écran concerné Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
32143c5bb1
commit
32582aba3d
@@ -0,0 +1,54 @@
|
||||
/// État global du didacticiel : quelles visites guidées restent à jouer.
|
||||
///
|
||||
/// Le provider est chargé au démarrage ; tant que les préférences ne sont pas
|
||||
/// lues, aucune visite n'est déclenchée (évite un flash d'overlay au lancement).
|
||||
library;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../../services/tutorial_service.dart';
|
||||
|
||||
class TutorialProvider with ChangeNotifier {
|
||||
TutorialProvider({TutorialService? service})
|
||||
: _service = service ?? TutorialService() {
|
||||
load();
|
||||
}
|
||||
|
||||
final TutorialService _service;
|
||||
|
||||
final Set<String> _completed = {};
|
||||
bool _loaded = false;
|
||||
|
||||
/// `true` une fois les préférences lues.
|
||||
bool get isLoaded => _loaded;
|
||||
|
||||
/// `true` si l'utilisateur a déjà terminé (ou passé) la visite d'accueil.
|
||||
bool get hasSeenIntro => _completed.contains(TutorialTours.home);
|
||||
|
||||
/// Lit les visites déjà vues. Les visites terminées pendant le chargement
|
||||
/// sont conservées (le résultat est fusionné, jamais écrasé).
|
||||
Future<void> load() async {
|
||||
_completed.addAll(await _service.loadCompletedTours());
|
||||
_loaded = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Faut-il jouer la visite [tourId] ?
|
||||
bool shouldRun(String tourId) => _loaded && !_completed.contains(tourId);
|
||||
|
||||
/// Marque une visite comme vue (terminée ou passée) : elle ne rejouera plus.
|
||||
Future<void> complete(String tourId) async {
|
||||
if (_completed.add(tourId)) {
|
||||
notifyListeners();
|
||||
await _service.markCompleted(tourId);
|
||||
}
|
||||
}
|
||||
|
||||
/// Relance le didacticiel depuis les paramètres : toutes les visites
|
||||
/// redeviennent disponibles et rejoueront dès l'affichage de leur écran.
|
||||
Future<void> restart() async {
|
||||
await _service.resetAll();
|
||||
_completed.clear();
|
||||
_loaded = true;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user