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:
qlionbleusam
2026-08-29 11:46:02 +02:00
co-authored by Claude Opus 5
parent 32143c5bb1
commit 32582aba3d
14 changed files with 1566 additions and 1 deletions
@@ -9,6 +9,7 @@ import '../../core/theme/theme_provider.dart';
import '../../core/widgets/glass_container.dart';
import '../../services/wallet_identity_service.dart';
import '../garage/weapon_list_screen.dart';
import '../tutorial/tutorial_provider.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@@ -708,6 +709,26 @@ class _SettingsScreenState extends State<SettingsScreen> {
);
}
/// Réactive le didacticiel : toutes les visites guidées sont remises à zéro
/// et celle de l'accueil redémarre dès le retour sur l'écran principal.
Future<void> _restartTutorial() async {
final tutorial = context.read<TutorialProvider>();
final messenger = ScaffoldMessenger.of(context);
final navigator = Navigator.of(context);
await tutorial.restart();
if (!mounted) return;
navigator.pop();
messenger.showSnackBar(
const SnackBar(
content: Text('Didacticiel réactivé : la visite guidée redémarre.'),
backgroundColor: AppTheme.successColor,
duration: Duration(seconds: 3),
),
);
}
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
@@ -997,6 +1018,22 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
],
const SizedBox(height: 20),
_buildSectionHeader('AIDE & DIDACTICIEL', primary),
Consumer<TutorialProvider>(
builder: (context, tutorial, child) {
return _buildSettingsTile(
context: context,
icon: Icons.school_outlined,
title: 'Revoir le didacticiel',
subtitle: tutorial.hasSeenIntro
? 'Rejouer la visite guidée de l\'application'
: 'Visite guidée en attente sur l\'écran d\'accueil',
onTap: _restartTutorial,
);
},
),
const SizedBox(height: 20),
_buildSectionHeader('À PROPOS', primary),
_buildSettingsTile(